From c4390486a6f198f82a5944eb1e123d9673331cf5 Mon Sep 17 00:00:00 2001 From: Codrin Ciubotariu Date: Fri, 24 Jul 2015 16:52:44 +0300 Subject: drivers/net/vsc9953: Remove 'CONFIG_' from macros' name Signed-off-by: Codrin Ciubotariu Acked-by: Joe Hershberger Reviewed-by: York Sun diff --git a/drivers/net/vsc9953.c b/drivers/net/vsc9953.c index fed7358..068ab86 100644 --- a/drivers/net/vsc9953.c +++ b/drivers/net/vsc9953.c @@ -202,26 +202,26 @@ void vsc9953_init(bd_t *bis) VSC9953_DEVCPU_GCB); out_le32(&l2dev_gcb->chip_regs.soft_rst, - CONFIG_VSC9953_SOFT_SWC_RST_ENA); + VSC9953_SOFT_SWC_RST_ENA); timeout = 50000; while ((in_le32(&l2dev_gcb->chip_regs.soft_rst) & - CONFIG_VSC9953_SOFT_SWC_RST_ENA) && --timeout) + VSC9953_SOFT_SWC_RST_ENA) && --timeout) udelay(1); /* busy wait for vsc9953 soft reset */ if (timeout == 0) debug("Timeout waiting for VSC9953 to reset\n"); - out_le32(&l2sys_reg->sys.reset_cfg, CONFIG_VSC9953_MEM_ENABLE | - CONFIG_VSC9953_MEM_INIT); + out_le32(&l2sys_reg->sys.reset_cfg, VSC9953_MEM_ENABLE | + VSC9953_MEM_INIT); timeout = 50000; while ((in_le32(&l2sys_reg->sys.reset_cfg) & - CONFIG_VSC9953_MEM_INIT) && --timeout) + VSC9953_MEM_INIT) && --timeout) udelay(1); /* busy wait for vsc9953 memory init */ if (timeout == 0) debug("Timeout waiting for VSC9953 memory to initialize\n"); out_le32(&l2sys_reg->sys.reset_cfg, (in_le32(&l2sys_reg->sys.reset_cfg) - | CONFIG_VSC9953_CORE_ENABLE)); + | VSC9953_CORE_ENABLE)); /* VSC9953 Setting to be done once only */ out_le32(&l2qsys_reg->sys.ext_cpu_cfg, 0x00000b00); @@ -233,34 +233,34 @@ void vsc9953_init(bd_t *bis) /* Enable VSC9953 GMII Ports Port ID 0 - 7 */ if (VSC9953_INTERNAL_PORT_CHECK(i)) { out_le32(&l2ana_reg->pfc[i].pfc_cfg, - CONFIG_VSC9953_PFC_FC_QSGMII); + VSC9953_PFC_FC_QSGMII); out_le32(&l2sys_reg->pause_cfg.mac_fc_cfg[i], - CONFIG_VSC9953_MAC_FC_CFG_QSGMII); + VSC9953_MAC_FC_CFG_QSGMII); } else { out_le32(&l2ana_reg->pfc[i].pfc_cfg, - CONFIG_VSC9953_PFC_FC); + VSC9953_PFC_FC); out_le32(&l2sys_reg->pause_cfg.mac_fc_cfg[i], - CONFIG_VSC9953_MAC_FC_CFG); + VSC9953_MAC_FC_CFG); } out_le32(&l2dev_gmii_reg->port_mode.clock_cfg, - CONFIG_VSC9953_CLOCK_CFG); + VSC9953_CLOCK_CFG); out_le32(&l2dev_gmii_reg->mac_cfg_status.mac_ena_cfg, - CONFIG_VSC9953_MAC_ENA_CFG); + VSC9953_MAC_ENA_CFG); out_le32(&l2dev_gmii_reg->mac_cfg_status.mac_mode_cfg, - CONFIG_VSC9953_MAC_MODE_CFG); + VSC9953_MAC_MODE_CFG); out_le32(&l2dev_gmii_reg->mac_cfg_status.mac_ifg_cfg, - CONFIG_VSC9953_MAC_IFG_CFG); + VSC9953_MAC_IFG_CFG); /* mac_hdx_cfg varies with port id*/ - hdx_cfg = (CONFIG_VSC9953_MAC_HDX_CFG | (i << 16)); + hdx_cfg = VSC9953_MAC_HDX_CFG | (i << 16); out_le32(&l2dev_gmii_reg->mac_cfg_status.mac_hdx_cfg, hdx_cfg); out_le32(&l2sys_reg->sys.front_port_mode[i], - CONFIG_VSC9953_FRONT_PORT_MODE); + VSC9953_FRONT_PORT_MODE); out_le32(&l2qsys_reg->sys.switch_port_mode[i], - CONFIG_VSC9953_PORT_ENA); + VSC9953_PORT_ENA); out_le32(&l2dev_gmii_reg->mac_cfg_status.mac_maxlen_cfg, - CONFIG_VSC9953_MAC_MAX_LEN); + VSC9953_MAC_MAX_LEN); out_le32(&l2sys_reg->pause_cfg.pause_cfg[i], - CONFIG_VSC9953_PAUSE_CFG); + VSC9953_PAUSE_CFG); /* WAIT FOR 2 us*/ udelay(2); diff --git a/include/vsc9953.h b/include/vsc9953.h index 3d11b87..3f14dad 100644 --- a/include/vsc9953.h +++ b/include/vsc9953.h @@ -33,29 +33,29 @@ #define T1040_SWITCH_GMII_DEV_OFFSET 0x010000 #define VSC9953_PHY_REGS_OFFST 0x0000AC -#define CONFIG_VSC9953_SOFT_SWC_RST_ENA 0x00000001 -#define CONFIG_VSC9953_CORE_ENABLE 0x80 -#define CONFIG_VSC9953_MEM_ENABLE 0x40 -#define CONFIG_VSC9953_MEM_INIT 0x20 - -#define CONFIG_VSC9953_PORT_ENA 0x00003a00 -#define CONFIG_VSC9953_MAC_ENA_CFG 0x00000011 -#define CONFIG_VSC9953_MAC_MODE_CFG 0x00000011 -#define CONFIG_VSC9953_MAC_IFG_CFG 0x00000515 -#define CONFIG_VSC9953_MAC_HDX_CFG 0x00001043 -#define CONFIG_VSC9953_CLOCK_CFG 0x00000001 -#define CONFIG_VSC9953_CLOCK_CFG_1000M 0x00000001 -#define CONFIG_VSC9953_PFC_FC 0x00000001 -#define CONFIG_VSC9953_PFC_FC_QSGMII 0x00000000 -#define CONFIG_VSC9953_MAC_FC_CFG 0x04700000 -#define CONFIG_VSC9953_MAC_FC_CFG_QSGMII 0x00700000 -#define CONFIG_VSC9953_PAUSE_CFG 0x001ffffe -#define CONFIG_VSC9953_TOT_TAIL_DROP_LVL 0x000003ff -#define CONFIG_VSC9953_FRONT_PORT_MODE 0x00000000 -#define CONFIG_VSC9953_MAC_MAX_LEN 0x000005ee - -#define CONFIG_VSC9953_VCAP_MV_CFG 0x0000ffff -#define CONFIG_VSC9953_VCAP_UPDATE_CTRL 0x01000004 +#define VSC9953_SOFT_SWC_RST_ENA 0x00000001 +#define VSC9953_CORE_ENABLE 0x80 +#define VSC9953_MEM_ENABLE 0x40 +#define VSC9953_MEM_INIT 0x20 + +#define VSC9953_PORT_ENA 0x00003a00 +#define VSC9953_MAC_ENA_CFG 0x00000011 +#define VSC9953_MAC_MODE_CFG 0x00000011 +#define VSC9953_MAC_IFG_CFG 0x00000515 +#define VSC9953_MAC_HDX_CFG 0x00001043 +#define VSC9953_CLOCK_CFG 0x00000001 +#define VSC9953_CLOCK_CFG_1000M 0x00000001 +#define VSC9953_PFC_FC 0x00000001 +#define VSC9953_PFC_FC_QSGMII 0x00000000 +#define VSC9953_MAC_FC_CFG 0x04700000 +#define VSC9953_MAC_FC_CFG_QSGMII 0x00700000 +#define VSC9953_PAUSE_CFG 0x001ffffe +#define VSC9953_TOT_TAIL_DROP_LVL 0x000003ff +#define VSC9953_FRONT_PORT_MODE 0x00000000 +#define VSC9953_MAC_MAX_LEN 0x000005ee + +#define VSC9953_VCAP_MV_CFG 0x0000ffff +#define VSC9953_VCAP_UPDATE_CTRL 0x01000004 #define VSC9953_MAX_PORTS 10 #define VSC9953_PORT_CHECK(port) \ (((port) < 0 || (port) >= VSC9953_MAX_PORTS) ? 0 : 1) -- cgit v0.10.2 From 3cc8cfffb21b618a51a51e746aac709a12540b79 Mon Sep 17 00:00:00 2001 From: Codrin Ciubotariu Date: Fri, 24 Jul 2015 16:52:45 +0300 Subject: drivers/net/vsc9953: Cleanup patch This patch groups some macros defined for registers and replaces some magic numbers from vsc9953 with macros. Also, "port" and "port_nr" words are replaced with "port_no", puts each variable declaration on a line and removes unnecessary tabs. Signed-off-by: Codrin Ciubotariu Acked-by: Joe Hershberger Reviewed-by: York Sun diff --git a/drivers/net/vsc9953.c b/drivers/net/vsc9953.c index 068ab86..524b979 100644 --- a/drivers/net/vsc9953.c +++ b/drivers/net/vsc9953.c @@ -10,6 +10,8 @@ #include #include #include +#include +#include #include static struct vsc9953_info vsc9953_l2sw = { @@ -25,50 +27,50 @@ static struct vsc9953_info vsc9953_l2sw = { .port[9] = VSC9953_PORT_INFO_INITIALIZER(9), }; -void vsc9953_port_info_set_mdio(int port, struct mii_dev *bus) +void vsc9953_port_info_set_mdio(int port_no, struct mii_dev *bus) { - if (!VSC9953_PORT_CHECK(port)) + if (!VSC9953_PORT_CHECK(port_no)) return; - vsc9953_l2sw.port[port].bus = bus; + vsc9953_l2sw.port[port_no].bus = bus; } -void vsc9953_port_info_set_phy_address(int port, int address) +void vsc9953_port_info_set_phy_address(int port_no, int address) { - if (!VSC9953_PORT_CHECK(port)) + if (!VSC9953_PORT_CHECK(port_no)) return; - vsc9953_l2sw.port[port].phyaddr = address; + vsc9953_l2sw.port[port_no].phyaddr = address; } -void vsc9953_port_info_set_phy_int(int port, phy_interface_t phy_int) +void vsc9953_port_info_set_phy_int(int port_no, phy_interface_t phy_int) { - if (!VSC9953_PORT_CHECK(port)) + if (!VSC9953_PORT_CHECK(port_no)) return; - vsc9953_l2sw.port[port].enet_if = phy_int; + vsc9953_l2sw.port[port_no].enet_if = phy_int; } -void vsc9953_port_enable(int port) +void vsc9953_port_enable(int port_no) { - if (!VSC9953_PORT_CHECK(port)) + if (!VSC9953_PORT_CHECK(port_no)) return; - vsc9953_l2sw.port[port].enabled = 1; + vsc9953_l2sw.port[port_no].enabled = 1; } -void vsc9953_port_disable(int port) +void vsc9953_port_disable(int port_no) { - if (!VSC9953_PORT_CHECK(port)) + if (!VSC9953_PORT_CHECK(port_no)) return; - vsc9953_l2sw.port[port].enabled = 0; + vsc9953_l2sw.port[port_no].enabled = 0; } static void vsc9953_mdio_write(struct vsc9953_mii_mng *phyregs, int port_addr, int regnum, int value) { - int timeout = 50000; + int timeout = 50000; out_le32(&phyregs->miimcmd, (0x1 << 31) | ((port_addr & 0x1f) << 25) | ((regnum & 0x1f) << 20) | ((value & 0xffff) << 4) | @@ -85,8 +87,8 @@ static void vsc9953_mdio_write(struct vsc9953_mii_mng *phyregs, int port_addr, static int vsc9953_mdio_read(struct vsc9953_mii_mng *phyregs, int port_addr, int regnum) { - int value = 0xFFFF; - int timeout = 50000; + int value = 0xFFFF; + int timeout = 50000; while ((in_le32(&phyregs->miimstatus) & MIIMIND_OPR_PEND) && --timeout) udelay(1); @@ -120,8 +122,8 @@ static int vsc9953_mdio_read(struct vsc9953_mii_mng *phyregs, int port_addr, static int init_phy(struct eth_device *dev) { - struct vsc9953_port_info *l2sw_port = dev->priv; - struct phy_device *phydev = NULL; + struct vsc9953_port_info *l2sw_port = dev->priv; + struct phy_device *phydev = NULL; #ifdef CONFIG_PHYLIB if (!l2sw_port->bus) @@ -148,21 +150,21 @@ static int init_phy(struct eth_device *dev) return 0; } -static int vsc9953_port_init(int port) +static int vsc9953_port_init(int port_no) { - struct eth_device *dev; + struct eth_device *dev; /* Internal ports never have a PHY */ - if (VSC9953_INTERNAL_PORT_CHECK(port)) + if (VSC9953_INTERNAL_PORT_CHECK(port_no)) return 0; /* alloc eth device */ dev = (struct eth_device *)calloc(1, sizeof(struct eth_device)); if (!dev) - return 1; + return -ENOMEM; - sprintf(dev->name, "SW@PORT%d", port); - dev->priv = &vsc9953_l2sw.port[port]; + sprintf(dev->name, "SW@PORT%d", port_no); + dev->priv = &vsc9953_l2sw.port[port_no]; dev->init = NULL; dev->halt = NULL; dev->send = NULL; @@ -170,7 +172,7 @@ static int vsc9953_port_init(int port) if (init_phy(dev)) { free(dev); - return 1; + return -ENODEV; } return 0; @@ -178,13 +180,15 @@ static int vsc9953_port_init(int port) void vsc9953_init(bd_t *bis) { - u32 i, hdx_cfg = 0, phy_addr = 0; - int timeout; - struct vsc9953_system_reg *l2sys_reg; - struct vsc9953_qsys_reg *l2qsys_reg; - struct vsc9953_dev_gmii *l2dev_gmii_reg; - struct vsc9953_analyzer *l2ana_reg; - struct vsc9953_devcpu_gcb *l2dev_gcb; + u32 i; + u32 hdx_cfg = 0; + u32 phy_addr = 0; + int timeout; + struct vsc9953_system_reg *l2sys_reg; + struct vsc9953_qsys_reg *l2qsys_reg; + struct vsc9953_dev_gmii *l2dev_gmii_reg; + struct vsc9953_analyzer *l2ana_reg; + struct vsc9953_devcpu_gcb *l2dev_gcb; l2dev_gmii_reg = (struct vsc9953_dev_gmii *)(VSC9953_OFFSET + VSC9953_DEV_GMII_OFFSET); @@ -312,83 +316,81 @@ void vsc9953_init(bd_t *bis) #ifdef CONFIG_VSC9953_CMD /* Enable/disable status of a VSC9953 port */ -static void vsc9953_port_status_set(int port_nr, u8 enabled) +static void vsc9953_port_status_set(int port_no, u8 enabled) { - u32 val; - struct vsc9953_qsys_reg *l2qsys_reg; + struct vsc9953_qsys_reg *l2qsys_reg; /* Administrative down */ - if (vsc9953_l2sw.port[port_nr].enabled == 0) + if (!vsc9953_l2sw.port[port_no].enabled) return; l2qsys_reg = (struct vsc9953_qsys_reg *)(VSC9953_OFFSET + VSC9953_QSYS_OFFSET); - val = in_le32(&l2qsys_reg->sys.switch_port_mode[port_nr]); - if (enabled == 1) - val |= (1 << 13); + if (enabled) + setbits_le32(&l2qsys_reg->sys.switch_port_mode[port_no], + VSC9953_PORT_ENA); else - val &= ~(1 << 13); - - out_le32(&l2qsys_reg->sys.switch_port_mode[port_nr], val); + clrbits_le32(&l2qsys_reg->sys.switch_port_mode[port_no], + VSC9953_PORT_ENA); } /* Set all VSC9953 ports' status */ static void vsc9953_port_all_status_set(u8 enabled) { - int i; + int i; for (i = 0; i < VSC9953_MAX_PORTS; i++) vsc9953_port_status_set(i, enabled); } /* Start autonegotiation for a VSC9953 PHY */ -static void vsc9953_phy_autoneg(int port_nr) +static void vsc9953_phy_autoneg(int port_no) { - if (!vsc9953_l2sw.port[port_nr].phydev) + if (!vsc9953_l2sw.port[port_no].phydev) return; - if (vsc9953_l2sw.port[port_nr].phydev->drv->startup( - vsc9953_l2sw.port[port_nr].phydev)) - printf("Failed to start PHY for port %d\n", port_nr); + if (vsc9953_l2sw.port[port_no].phydev->drv->startup( + vsc9953_l2sw.port[port_no].phydev)) + printf("Failed to start PHY for port %d\n", port_no); } /* Start autonegotiation for all VSC9953 PHYs */ static void vsc9953_phy_all_autoneg(void) { - int i; + int i; for (i = 0; i < VSC9953_MAX_PORTS; i++) vsc9953_phy_autoneg(i); } /* Print a VSC9953 port's configuration */ -static void vsc9953_port_config_show(int port) +static void vsc9953_port_config_show(int port_no) { - int speed; - int duplex; - int link; - u8 enabled; - u32 val; + int speed; + int duplex; + int link; + u8 enabled; + u32 val; struct vsc9953_qsys_reg *l2qsys_reg; l2qsys_reg = (struct vsc9953_qsys_reg *)(VSC9953_OFFSET + VSC9953_QSYS_OFFSET); - val = in_le32(&l2qsys_reg->sys.switch_port_mode[port]); - enabled = vsc9953_l2sw.port[port].enabled & - ((val & 0x00002000) >> 13); + val = in_le32(&l2qsys_reg->sys.switch_port_mode[port_no]); + enabled = vsc9953_l2sw.port[port_no].enabled && + (val & VSC9953_PORT_ENA); /* internal ports (8 and 9) are fixed */ - if (VSC9953_INTERNAL_PORT_CHECK(port)) { + if (VSC9953_INTERNAL_PORT_CHECK(port_no)) { link = 1; speed = SPEED_2500; duplex = DUPLEX_FULL; } else { - if (vsc9953_l2sw.port[port].phydev) { - link = vsc9953_l2sw.port[port].phydev->link; - speed = vsc9953_l2sw.port[port].phydev->speed; - duplex = vsc9953_l2sw.port[port].phydev->duplex; + if (vsc9953_l2sw.port[port_no].phydev) { + link = vsc9953_l2sw.port[port_no].phydev->link; + speed = vsc9953_l2sw.port[port_no].phydev->speed; + duplex = vsc9953_l2sw.port[port_no].phydev->duplex; } else { link = -1; speed = -1; @@ -396,7 +398,7 @@ static void vsc9953_port_config_show(int port) } } - printf("%8d ", port); + printf("%8d ", port_no); printf("%8s ", enabled == 1 ? "enabled" : "disabled"); printf("%8s ", link == 1 ? "up" : "down"); @@ -426,7 +428,7 @@ static void vsc9953_port_config_show(int port) /* Print VSC9953 ports' configuration */ static void vsc9953_port_all_config_show(void) { - int i; + int i; for (i = 0; i < VSC9953_MAX_PORTS; i++) vsc9953_port_config_show(i); @@ -487,11 +489,11 @@ static int do_ethsw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) U_BOOT_CMD(ethsw, 5, 0, do_ethsw, "vsc9953 l2 switch commands", - "port enable|disable\n" + "port enable|disable\n" " - enable/disable an l2 switch port\n" - " port_nr=0..9; use \"all\" for all ports\n" - "ethsw port show\n" + " port_no=0..9; use \"all\" for all ports\n" + "ethsw port show\n" " - show an l2 switch port's configuration\n" - " port_nr=0..9; use \"all\" for all ports\n" + " port_no=0..9; use \"all\" for all ports\n" ); #endif /* CONFIG_VSC9953_CMD */ diff --git a/include/vsc9953.h b/include/vsc9953.h index 3f14dad..6ebe141 100644 --- a/include/vsc9953.h +++ b/include/vsc9953.h @@ -17,7 +17,6 @@ #include #include #include -#include #define VSC9953_OFFSET (CONFIG_SYS_CCSRBAR_DEFAULT + 0x800000) @@ -33,29 +32,57 @@ #define T1040_SWITCH_GMII_DEV_OFFSET 0x010000 #define VSC9953_PHY_REGS_OFFST 0x0000AC +/* Macros for vsc9953_chip_regs.soft_rst register */ #define VSC9953_SOFT_SWC_RST_ENA 0x00000001 + +/* Macros for vsc9953_sys_sys.reset_cfg register */ #define VSC9953_CORE_ENABLE 0x80 #define VSC9953_MEM_ENABLE 0x40 #define VSC9953_MEM_INIT 0x20 -#define VSC9953_PORT_ENA 0x00003a00 +/* Macros for vsc9953_dev_gmii_mac_cfg_status.mac_ena_cfg register */ #define VSC9953_MAC_ENA_CFG 0x00000011 + +/* Macros for vsc9953_dev_gmii_mac_cfg_status.mac_mode_cfg register */ #define VSC9953_MAC_MODE_CFG 0x00000011 + +/* Macros for vsc9953_dev_gmii_mac_cfg_status.mac_ifg_cfg register */ #define VSC9953_MAC_IFG_CFG 0x00000515 + +/* Macros for vsc9953_dev_gmii_mac_cfg_status.mac_hdx_cfg register */ #define VSC9953_MAC_HDX_CFG 0x00001043 + +/* Macros for vsc9953_dev_gmii_mac_cfg_status.mac_maxlen_cfg register */ +#define VSC9953_MAC_MAX_LEN 0x000005ee + +/* Macros for vsc9953_dev_gmii_port_mode.clock_cfg register */ #define VSC9953_CLOCK_CFG 0x00000001 #define VSC9953_CLOCK_CFG_1000M 0x00000001 + +/* Macros for vsc9953_sys_sys.front_port_mode register */ +#define VSC9953_FRONT_PORT_MODE 0x00000000 + +/* Macros for vsc9953_ana_pfc.pfc_cfg register */ #define VSC9953_PFC_FC 0x00000001 #define VSC9953_PFC_FC_QSGMII 0x00000000 + +/* Macros for vsc9953_sys_pause_cfg.mac_fc_cfg register */ #define VSC9953_MAC_FC_CFG 0x04700000 #define VSC9953_MAC_FC_CFG_QSGMII 0x00700000 + +/* Macros for vsc9953_sys_pause_cfg.pause_cfg register */ #define VSC9953_PAUSE_CFG 0x001ffffe + +/* Macros for vsc9953_sys_pause_cfgtot_tail_drop_lvl register */ #define VSC9953_TOT_TAIL_DROP_LVL 0x000003ff -#define VSC9953_FRONT_PORT_MODE 0x00000000 -#define VSC9953_MAC_MAX_LEN 0x000005ee +/* Macros for vsc9953_vcap_core_cfg.vcap_mv_cfg register */ #define VSC9953_VCAP_MV_CFG 0x0000ffff #define VSC9953_VCAP_UPDATE_CTRL 0x01000004 + +/* Macros for vsc9953_qsys_sys.switch_port_mode register */ +#define VSC9953_PORT_ENA 0x00003a00 + #define VSC9953_MAX_PORTS 10 #define VSC9953_PORT_CHECK(port) \ (((port) < 0 || (port) >= VSC9953_MAX_PORTS) ? 0 : 1) @@ -74,9 +101,9 @@ struct vsc9953_mdio_info { char *name; }; -/* VSC9953 ANA structure for T1040 U-boot*/ +/* VSC9953 ANA structure */ -struct vsc9953_ana_port { +struct vsc9953_ana_port { u32 vlan_cfg; u32 drop_cfg; u32 qos_cfg; @@ -138,7 +165,7 @@ struct vsc9953_ana_pgid { u32 port_grp_id[91]; }; -struct vsc9953_ana_pfc { +struct vsc9953_ana_pfc { u32 pfc_cfg; u32 reserved1[15]; }; @@ -149,7 +176,7 @@ struct vsc9953_ana_pol_misc { u32 pol_hyst; }; -struct vsc9953_ana_common { +struct vsc9953_ana_common { u32 aggr_cfg; u32 cpuq_cfg; u32 cpuq_8021_cfg; @@ -176,18 +203,18 @@ struct vsc9953_analyzer { u32 reserved5[196]; struct vsc9953_ana_common common; }; -/* END VSC9953 ANA structure for T1040 U-boot*/ +/* END VSC9953 ANA structure t*/ -/* VSC9953 DEV_GMII structure for T1040 U-boot*/ +/* VSC9953 DEV_GMII structure */ -struct vsc9953_dev_gmii_port_mode { +struct vsc9953_dev_gmii_port_mode { u32 clock_cfg; u32 port_misc; u32 reserved1; u32 eee_cfg; }; -struct vsc9953_dev_gmii_mac_cfg_status { +struct vsc9953_dev_gmii_mac_cfg_status { u32 mac_ena_cfg; u32 mac_mode_cfg; u32 mac_maxlen_cfg; @@ -205,11 +232,11 @@ struct vsc9953_dev_gmii { struct vsc9953_dev_gmii_mac_cfg_status mac_cfg_status; }; -/* END VSC9953 DEV_GMII structure for T1040 U-boot*/ +/* END VSC9953 DEV_GMII structure */ -/* VSC9953 QSYS structure for T1040 U-boot*/ +/* VSC9953 QSYS structure */ -struct vsc9953_qsys_hsch { +struct vsc9953_qsys_hsch { u32 cir_cfg; u32 reserved1; u32 se_cfg; @@ -218,7 +245,7 @@ struct vsc9953_qsys_hsch { u32 reserved2[20]; }; -struct vsc9953_qsys_sys { +struct vsc9953_qsys_sys { u32 port_mode[12]; u32 switch_port_mode[11]; u32 stat_cnt_cfg; @@ -232,32 +259,32 @@ struct vsc9953_qsys_sys { u32 reserved1[23]; }; -struct vsc9953_qsys_qos_cfg { +struct vsc9953_qsys_qos_cfg { u32 red_profile[16]; u32 res_qos_mode; }; -struct vsc9953_qsys_drop_cfg { +struct vsc9953_qsys_drop_cfg { u32 egr_drop_mode; }; -struct vsc9953_qsys_mmgt { +struct vsc9953_qsys_mmgt { u32 eq_cntrl; u32 reserved1; }; -struct vsc9953_qsys_hsch_misc { +struct vsc9953_qsys_hsch_misc { u32 hsch_misc_cfg; u32 reserved1[546]; }; -struct vsc9953_qsys_res_ctrl { +struct vsc9953_qsys_res_ctrl { u32 res_cfg; u32 res_stat; }; -struct vsc9953_qsys_reg { +struct vsc9953_qsys_reg { struct vsc9953_qsys_hsch hsch[108]; struct vsc9953_qsys_sys sys; struct vsc9953_qsys_qos_cfg qos_cfg; @@ -267,18 +294,18 @@ struct vsc9953_qsys_reg { struct vsc9953_qsys_res_ctrl res_ctrl[1024]; }; -/* END VSC9953 QSYS structure for T1040 U-boot*/ +/* END VSC9953 QSYS structure */ -/* VSC9953 SYS structure for T1040 U-boot*/ +/* VSC9953 SYS structure */ -struct vsc9953_sys_stat { +struct vsc9953_sys_stat { u32 rx_cntrs[64]; u32 tx_cntrs[64]; u32 drop_cntrs[64]; u32 reserved1[6]; }; -struct vsc9953_sys_sys { +struct vsc9953_sys_sys { u32 reset_cfg; u32 reserved1; u32 vlan_etype_cfg; @@ -289,7 +316,7 @@ struct vsc9953_sys_sys { u32 reserved2[50]; }; -struct vsc9953_sys_pause_cfg { +struct vsc9953_sys_pause_cfg { u32 pause_cfg[11]; u32 pause_tot_cfg; u32 tail_drop_level[11]; @@ -297,29 +324,29 @@ struct vsc9953_sys_pause_cfg { u32 mac_fc_cfg[10]; }; -struct vsc9953_sys_mmgt { +struct vsc9953_sys_mmgt { u16 free_cnt; }; -struct vsc9953_system_reg { +struct vsc9953_system_reg { struct vsc9953_sys_stat stat; struct vsc9953_sys_sys sys; struct vsc9953_sys_pause_cfg pause_cfg; struct vsc9953_sys_mmgt mmgt; }; -/* END VSC9953 SYS structure for T1040 U-boot*/ +/* END VSC9953 SYS structure */ -/* VSC9953 DEVCPU_GCB structure for T1040 U-boot*/ +/* VSC9953 DEVCPU_GCB structure */ -struct vsc9953_chip_regs { +struct vsc9953_chip_regs { u32 chipd_id; u32 gpr; u32 soft_rst; }; -struct vsc9953_gpio { +struct vsc9953_gpio { u32 gpio_out_set[10]; u32 gpio_out_clr[10]; u32 gpio_out[10]; @@ -338,31 +365,31 @@ struct vsc9953_mii_mng { u32 miiscan_lst_rslts_valid; }; -struct vsc9953_mii_read_scan { +struct vsc9953_mii_read_scan { u32 mii_scan_results_sticky[2]; }; -struct vsc9953_devcpu_gcb { +struct vsc9953_devcpu_gcb { struct vsc9953_chip_regs chip_regs; struct vsc9953_gpio gpio; struct vsc9953_mii_mng mii_mng[2]; struct vsc9953_mii_read_scan mii_read_scan; }; -/* END VSC9953 DEVCPU_GCB structure for T1040 U-boot*/ +/* END VSC9953 DEVCPU_GCB structure */ -/* VSC9953 IS* structure for T1040 U-boot*/ +/* VSC9953 IS* structure */ -struct vsc9953_vcap_core_cfg { +struct vsc9953_vcap_core_cfg { u32 vcap_update_ctrl; u32 vcap_mv_cfg; }; -struct vsc9953_vcap { -struct vsc9953_vcap_core_cfg vcap_core_cfg; +struct vsc9953_vcap { + struct vsc9953_vcap_core_cfg vcap_core_cfg; }; -/* END VSC9953 IS* structure for T1040 U-boot*/ +/* END VSC9953 IS* structure */ #define VSC9953_PORT_INFO_INITIALIZER(idx) \ { \ @@ -388,15 +415,15 @@ struct vsc9953_port_info { /* Structure to describe a VSC9953 switch */ struct vsc9953_info { - struct vsc9953_port_info port[VSC9953_MAX_PORTS]; + struct vsc9953_port_info port[VSC9953_MAX_PORTS]; }; void vsc9953_init(bd_t *bis); -void vsc9953_port_info_set_mdio(int port, struct mii_dev *bus); -void vsc9953_port_info_set_phy_address(int port, int address); -void vsc9953_port_enable(int port); -void vsc9953_port_disable(int port); -void vsc9953_port_info_set_phy_int(int port, phy_interface_t phy_int); +void vsc9953_port_info_set_mdio(int port_no, struct mii_dev *bus); +void vsc9953_port_info_set_phy_address(int port_no, int address); +void vsc9953_port_enable(int port_no); +void vsc9953_port_disable(int port_no); +void vsc9953_port_info_set_phy_int(int port_no, phy_interface_t phy_int); #endif /* _VSC9953_H_ */ -- cgit v0.10.2 From fe91095b799d21428bd39392650ca0b06789776e Mon Sep 17 00:00:00 2001 From: Codrin Ciubotariu Date: Fri, 24 Jul 2015 16:52:46 +0300 Subject: drivers/net/vsc9953: Fix bug when enabling a port When a port is enabled at init time, the initializing function touches more bits than necessary to enable a port (also touches reserved bits and default bit values). This patch fixes this issue by changing the value of the define used to enable the port and assures that no other bits are changes by replacing out_le32() with setbits_le32(). Signed-off-by: Codrin Ciubotariu Acked-by: Joe Hershberger Reviewed-by: York Sun diff --git a/drivers/net/vsc9953.c b/drivers/net/vsc9953.c index 524b979..2e8eec4 100644 --- a/drivers/net/vsc9953.c +++ b/drivers/net/vsc9953.c @@ -259,8 +259,8 @@ void vsc9953_init(bd_t *bis) out_le32(&l2dev_gmii_reg->mac_cfg_status.mac_hdx_cfg, hdx_cfg); out_le32(&l2sys_reg->sys.front_port_mode[i], VSC9953_FRONT_PORT_MODE); - out_le32(&l2qsys_reg->sys.switch_port_mode[i], - VSC9953_PORT_ENA); + setbits_le32(&l2qsys_reg->sys.switch_port_mode[i], + VSC9953_PORT_ENA); out_le32(&l2dev_gmii_reg->mac_cfg_status.mac_maxlen_cfg, VSC9953_MAC_MAX_LEN); out_le32(&l2sys_reg->pause_cfg.pause_cfg[i], diff --git a/include/vsc9953.h b/include/vsc9953.h index 6ebe141..6ae5fec 100644 --- a/include/vsc9953.h +++ b/include/vsc9953.h @@ -81,7 +81,7 @@ #define VSC9953_VCAP_UPDATE_CTRL 0x01000004 /* Macros for vsc9953_qsys_sys.switch_port_mode register */ -#define VSC9953_PORT_ENA 0x00003a00 +#define VSC9953_PORT_ENA 0x00002000 #define VSC9953_MAX_PORTS 10 #define VSC9953_PORT_CHECK(port) \ -- cgit v0.10.2 From 440873dfc44eec4f92c482068c8be4c9f900032f Mon Sep 17 00:00:00 2001 From: Codrin Ciubotariu Date: Fri, 24 Jul 2015 16:55:24 +0300 Subject: drivers/net/vsc9953: Fix missing reserved register The VSC9953 DS reserves a register between vlan_mask and anag_efil registers. Signed-off-by: Johnson Leung Signed-off-by: Codrin Ciubotariu Acked-by: Joe Hershberger Reviewed-by: York Sun diff --git a/include/vsc9953.h b/include/vsc9953.h index 6ae5fec..8ff02c1 100644 --- a/include/vsc9953.h +++ b/include/vsc9953.h @@ -143,6 +143,7 @@ struct vsc9953_ana_ana_tables { struct vsc9953_ana_ana { u32 adv_learn; u32 vlan_mask; + u32 reserved; u32 anag_efil; u32 an_events; u32 storm_limit_burst; -- cgit v0.10.2 From 8756de2824e7c282afca59db3706aa5d473a7067 Mon Sep 17 00:00:00 2001 From: Codrin Ciubotariu Date: Fri, 24 Jul 2015 16:55:25 +0300 Subject: include/bitfield: Add new bitfield operations These new operations allow manipulation of bitfields within a word by using a mask instead of width and shift values to extract/replace the bitfields. Signed-off-by: Codrin Ciubotariu Acked-by: Joe Hershberger Reviewed-by: York Sun diff --git a/include/bitfield.h b/include/bitfield.h index b884c74..a59f3c2 100644 --- a/include/bitfield.h +++ b/include/bitfield.h @@ -27,6 +27,12 @@ * old = bitfield_extract(old_reg_val, 10, 5); * new_reg_val = bitfield_replace(old_reg_val, 10, 5, new); * + * or + * + * mask = bitfield_mask(10, 5); + * old = bitfield_extract_by_mask(old_reg_val, mask); + * new_reg_val = bitfield_replace_by_mask(old_reg_val, mask, new); + * * The numbers 10 and 5 could for example come from data * tables which describe all bitfields in all registers. */ @@ -56,3 +62,29 @@ static inline uint bitfield_replace(uint reg_val, uint shift, uint width, return (reg_val & ~mask) | ((bitfield_val << shift) & mask); } + +/* Produces a shift of the bitfield given a mask */ +static inline uint bitfield_shift(uint mask) +{ + return mask ? ffs(mask) - 1 : 0; +} + +/* Extract the value of a bitfield found within a given register value */ +static inline uint bitfield_extract_by_mask(uint reg_val, uint mask) +{ + uint shift = bitfield_shift(mask); + + return (reg_val & mask) >> shift; +} + +/* + * Replace the value of a bitfield found within a given register value + * Returns the newly modified uint value with the replaced field. + */ +static inline uint bitfield_replace_by_mask(uint reg_val, uint mask, + uint bitfield_val) +{ + uint shift = bitfield_shift(mask); + + return (reg_val & ~mask) | ((bitfield_val << shift) & mask); +} -- cgit v0.10.2 From 9de059871f8364dd898faf0b6057b8fa69fa2728 Mon Sep 17 00:00:00 2001 From: Codrin Ciubotariu Date: Fri, 24 Jul 2015 16:55:26 +0300 Subject: drivers/net/vsc9953: Add default configuration for VSC9953 L2 Switch At startup, the default configuration should be: - enable HW learning on all ports (HW default); - all ports are VLAN aware; - all ports are members of VLAN 1; - all ports have Port-based VLAN 1; - on all ports, the switch is allowed to remove maximum one VLAN tag, - on egress, the switch should add a VLAN tag if the frame is classified to a different VLAN than the port's Port-based VLAN; Signed-off-by: Johnson Leung Signed-off-by: Codrin Ciubotariu Acked-by: Joe Hershberger Reviewed-by: York Sun diff --git a/drivers/net/vsc9953.c b/drivers/net/vsc9953.c index 2e8eec4..59e0fab 100644 --- a/drivers/net/vsc9953.c +++ b/drivers/net/vsc9953.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -178,6 +179,256 @@ static int vsc9953_port_init(int port_no) return 0; } +static int vsc9953_vlan_table_poll_idle(void) +{ + struct vsc9953_analyzer *l2ana_reg; + int timeout; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + timeout = 50000; + while (((in_le32(&l2ana_reg->ana_tables.vlan_access) & + VSC9953_VLAN_CMD_MASK) != VSC9953_VLAN_CMD_IDLE) && --timeout) + udelay(1); + + return timeout ? 0 : -EBUSY; +} + +/* vlan table set/clear all membership of vid */ +static void vsc9953_vlan_table_membership_all_set(int vid, int set_member) +{ + uint 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; + } + + /* read current vlan configuration */ + val = in_le32(&l2ana_reg->ana_tables.vlan_tidx); + out_le32(&l2ana_reg->ana_tables.vlan_tidx, + bitfield_replace_by_mask(val, VSC9953_ANA_TBL_VID_MASK, vid)); + + 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); + out_le32(&l2ana_reg->ana_tables.vlan_tidx, + bitfield_replace_by_mask(val, VSC9953_ANA_TBL_VID_MASK, vid)); + + clrsetbits_le32(&l2ana_reg->ana_tables.vlan_access, + VSC9953_VLAN_PORT_MASK | VSC9953_VLAN_CMD_MASK, + VSC9953_VLAN_CMD_WRITE | + (set_member ? VSC9953_VLAN_PORT_MASK : 0)); +} + +/* Set PVID for a VSC9953 port */ +static void vsc9953_port_vlan_pvid_set(int port_no, int pvid) +{ + uint val; + struct vsc9953_analyzer *l2ana_reg; + 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; + } + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + l2rew_reg = (struct vsc9953_rew_reg *)(VSC9953_OFFSET + + VSC9953_REW_OFFSET); + + /* Set PVID on ingress */ + val = in_le32(&l2ana_reg->port[port_no].vlan_cfg); + val = bitfield_replace_by_mask(val, VSC9953_VLAN_CFG_VID_MASK, pvid); + out_le32(&l2ana_reg->port[port_no].vlan_cfg, val); + + /* Set PVID on egress */ + val = in_le32(&l2rew_reg->port[port_no].port_vlan_cfg); + val = bitfield_replace_by_mask(val, VSC9953_PORT_VLAN_CFG_VID_MASK, + pvid); + out_le32(&l2rew_reg->port[port_no].port_vlan_cfg, val); +} + +static void vsc9953_port_all_vlan_pvid_set(int pvid) +{ + int i; + + for (i = 0; i < VSC9953_MAX_PORTS; i++) + vsc9953_port_vlan_pvid_set(i, pvid); +} + +/* Enable/disable vlan aware of a VSC9953 port */ +static void vsc9953_port_vlan_aware_set(int port_no, int enabled) +{ + 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); + + if (enabled) + setbits_le32(&l2ana_reg->port[port_no].vlan_cfg, + VSC9953_VLAN_CFG_AWARE_ENA); + else + clrbits_le32(&l2ana_reg->port[port_no].vlan_cfg, + VSC9953_VLAN_CFG_AWARE_ENA); +} + +/* Set all VSC9953 ports' vlan aware */ +static void vsc9953_port_all_vlan_aware_set(int enabled) +{ + int i; + + for (i = 0; i < VSC9953_MAX_PORTS; i++) + vsc9953_port_vlan_aware_set(i, enabled); +} + +/* Enable/disable vlan pop count of a VSC9953 port */ +static void vsc9953_port_vlan_popcnt_set(int port_no, int popcnt) +{ + uint 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; + } + + if (popcnt > 3 || popcnt < 0) { + printf("Invalid pop count value: %d\n", port_no); + return; + } + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + val = in_le32(&l2ana_reg->port[port_no].vlan_cfg); + val = bitfield_replace_by_mask(val, VSC9953_VLAN_CFG_POP_CNT_MASK, + popcnt); + out_le32(&l2ana_reg->port[port_no].vlan_cfg, val); +} + +/* Set all VSC9953 ports' pop count */ +static void vsc9953_port_all_vlan_poncnt_set(int popcnt) +{ + int i; + + for (i = 0; i < VSC9953_MAX_PORTS; i++) + vsc9953_port_vlan_popcnt_set(i, popcnt); +} + +/* Enable/disable learning for frames dropped due to ingress filtering */ +static void vsc9953_vlan_ingr_fltr_learn_drop(int enable) +{ + struct vsc9953_analyzer *l2ana_reg; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + if (enable) + setbits_le32(&l2ana_reg->ana.adv_learn, VSC9953_VLAN_CHK); + else + clrbits_le32(&l2ana_reg->ana.adv_learn, VSC9953_VLAN_CHK); +} + +/* Egress untag modes of a VSC9953 port */ +enum egress_untag_mode { + EGRESS_UNTAG_ALL = 0, + EGRESS_UNTAG_PVID_AND_ZERO, + EGRESS_UNTAG_ZERO, + EGRESS_UNTAG_NONE, +}; + +static void vsc9953_port_vlan_egr_untag_set(int port_no, + enum egress_untag_mode mode) +{ + 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; + } + + l2rew_reg = (struct vsc9953_rew_reg *)(VSC9953_OFFSET + + VSC9953_REW_OFFSET); + + switch (mode) { + case EGRESS_UNTAG_ALL: + clrsetbits_le32(&l2rew_reg->port[port_no].port_tag_cfg, + VSC9953_TAG_CFG_MASK, VSC9953_TAG_CFG_NONE); + break; + case EGRESS_UNTAG_PVID_AND_ZERO: + clrsetbits_le32(&l2rew_reg->port[port_no].port_tag_cfg, + VSC9953_TAG_CFG_MASK, + VSC9953_TAG_CFG_ALL_BUT_PVID_ZERO); + break; + case EGRESS_UNTAG_ZERO: + clrsetbits_le32(&l2rew_reg->port[port_no].port_tag_cfg, + VSC9953_TAG_CFG_MASK, + VSC9953_TAG_CFG_ALL_BUT_ZERO); + break; + case EGRESS_UNTAG_NONE: + clrsetbits_le32(&l2rew_reg->port[port_no].port_tag_cfg, + VSC9953_TAG_CFG_MASK, VSC9953_TAG_CFG_ALL); + break; + default: + printf("Unknown untag mode for port %d\n", port_no); + } +} + +static void vsc9953_port_all_vlan_egress_untagged_set( + enum egress_untag_mode mode) +{ + int i; + + for (i = 0; i < VSC9953_MAX_PORTS; i++) + vsc9953_port_vlan_egr_untag_set(i, mode); +} + +/***************************************************************************** +At startup, the default configuration would be: + - HW learning enabled on all ports; (HW default) + - All ports are in VLAN 1; + - All ports are VLAN aware; + - All ports have POP_COUNT 1; + - All ports have PVID 1; + - All ports have TPID 0x8100; (HW default) + - All ports tag frames classified to all VLANs that are not PVID; +*****************************************************************************/ +void vsc9953_default_configuration(void) +{ + int i; + + for (i = 0; i < VSC9953_MAX_VLAN; i++) + vsc9953_vlan_table_membership_all_set(i, 0); + vsc9953_port_all_vlan_aware_set(1); + vsc9953_port_all_vlan_pvid_set(1); + vsc9953_port_all_vlan_poncnt_set(1); + 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); +} + void vsc9953_init(bd_t *bis) { u32 i; @@ -310,6 +561,8 @@ void vsc9953_init(bd_t *bis) } } + vsc9953_default_configuration(); + printf("VSC9953 L2 switch initialized\n"); return; } diff --git a/include/vsc9953.h b/include/vsc9953.h index 8ff02c1..bb9e22d 100644 --- a/include/vsc9953.h +++ b/include/vsc9953.h @@ -21,6 +21,7 @@ #define VSC9953_OFFSET (CONFIG_SYS_CCSRBAR_DEFAULT + 0x800000) #define VSC9953_SYS_OFFSET 0x010000 +#define VSC9953_REW_OFFSET 0x030000 #define VSC9953_DEV_GMII_OFFSET 0x100000 #define VSC9953_QSYS_OFFSET 0x200000 #define VSC9953_ANA_OFFSET 0x280000 @@ -80,9 +81,38 @@ #define VSC9953_VCAP_MV_CFG 0x0000ffff #define VSC9953_VCAP_UPDATE_CTRL 0x01000004 +/* 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_VID_MASK 0x00000fff + +/* Macros for vsc9953_rew_port.port_vlan_cfg register */ +#define VSC9953_PORT_VLAN_CFG_VID_MASK 0x00000fff + +/* Macros for vsc9953_ana_ana_tables.vlan_tidx register */ +#define VSC9953_ANA_TBL_VID_MASK 0x00000fff + +/* Macros for vsc9953_ana_ana_tables.vlan_access register */ +#define VSC9953_VLAN_PORT_MASK 0x00001ffc +#define VSC9953_VLAN_CMD_MASK 0x00000003 +#define VSC9953_VLAN_CMD_IDLE 0x00000000 +#define VSC9953_VLAN_CMD_READ 0x00000001 +#define VSC9953_VLAN_CMD_WRITE 0x00000002 +#define VSC9953_VLAN_CMD_INIT 0x00000003 + /* Macros for vsc9953_qsys_sys.switch_port_mode register */ #define VSC9953_PORT_ENA 0x00002000 +/* Macros for vsc9953_ana_ana.adv_learn register */ +#define VSC9953_VLAN_CHK 0x00000400 + +/* Macros for vsc9953_rew_port.port_tag_cfg register */ +#define VSC9953_TAG_CFG_MASK 0x00000180 +#define VSC9953_TAG_CFG_NONE 0x00000000 +#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_MAX_PORTS 10 #define VSC9953_PORT_CHECK(port) \ (((port) < 0 || (port) >= VSC9953_MAX_PORTS) ? 0 : 1) @@ -91,6 +121,9 @@ (port) < VSC9953_MAX_PORTS - 2 || (port) >= VSC9953_MAX_PORTS \ ) ? 0 : 1 \ ) +#define VSC9953_MAX_VLAN 4096 +#define VSC9953_VLAN_CHECK(vid) \ + (((vid) < 0 || (vid) >= VSC9953_MAX_VLAN) ? 0 : 1) #define DEFAULT_VSC9953_MDIO_NAME "VSC9953_MDIO0" @@ -338,6 +371,29 @@ struct vsc9953_system_reg { /* END VSC9953 SYS structure */ +/* VSC9953 REW structure */ + +struct vsc9953_rew_port { + u32 port_vlan_cfg; + u32 port_tag_cfg; + u32 port_port_cfg; + u32 port_dscp_cfg; + u32 port_pcp_dei_qos_map_cfg[16]; + u32 reserved[12]; +}; + +struct vsc9953_rew_common { + u32 reserve[4]; + u32 dscp_remap_dp1_cfg[64]; + u32 dscp_remap_cfg[64]; +}; + +struct vsc9953_rew_reg { + struct vsc9953_rew_port port[12]; + struct vsc9953_rew_common common; +}; + +/* END VSC9953 REW structure */ /* VSC9953 DEVCPU_GCB structure */ -- cgit v0.10.2 From 4ea54e3f2394cfca9ffaa14c181d2ae8a11677a8 Mon Sep 17 00:00:00 2001 From: Codrin Ciubotariu Date: Fri, 24 Jul 2015 16:55:27 +0300 Subject: common/cmd_ethsw: Add generic commands for Ethernet Switches This patch creates a flexible parser for Ethernet Switch configurations that should support complex commands. The parser searches for predefined keywords in the command and calls the proper function when a match is found. Also, the parser allows for optional keywords, such as "port", to apply the command on a port or on all ports. For now, the defined commands are: ethsw [port ] { enable | disable | show } Signed-off-by: Codrin Ciubotariu Reviewed-by: York Sun diff --git a/common/Makefile b/common/Makefile index 556fb07..491c565 100644 --- a/common/Makefile +++ b/common/Makefile @@ -214,6 +214,7 @@ obj-$(CONFIG_DFU_TFTP) += update.o obj-$(CONFIG_USB_KEYBOARD) += usb_kbd.o obj-$(CONFIG_CMD_DFU) += cmd_dfu.o obj-$(CONFIG_CMD_GPT) += cmd_gpt.o +obj-$(CONFIG_CMD_ETHSW) += cmd_ethsw.o # Power obj-$(CONFIG_CMD_PMIC) += cmd_pmic.o diff --git a/common/cmd_ethsw.c b/common/cmd_ethsw.c new file mode 100644 index 0000000..9a7ae74 --- /dev/null +++ b/common/cmd_ethsw.c @@ -0,0 +1,347 @@ +/* + * Copyright 2015 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Ethernet Switch commands + */ + +#include +#include +#include +#include + +static const char *ethsw_name; + +static struct keywords_to_function { + enum ethsw_keyword_id cmd_keyword[ETHSW_MAX_CMD_PARAMS]; + int cmd_func_offset; + int (*keyword_function)(struct ethsw_command_def *parsed_cmd); +} ethsw_cmd_def[] = { + { + .cmd_keyword = { + ethsw_id_enable, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_enable), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_disable, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_disable), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_show, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_show), + .keyword_function = NULL, + }, +}; + +struct keywords_optional { + int cmd_keyword[ETHSW_MAX_CMD_PARAMS]; +} cmd_opt_def[] = { + { + .cmd_keyword = { + ethsw_id_port, + ethsw_id_port_no, + ethsw_id_key_end, + }, + }, +}; + +static int keyword_match_gen(enum ethsw_keyword_id key_id, int argc, char + *const argv[], int *argc_nr, + struct ethsw_command_def *parsed_cmd); +static int keyword_match_port(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; + * keep the order synced with enum ethsw_keyword_id + */ +struct keyword_def { + const char *keyword_name; + int (*match)(enum ethsw_keyword_id key_id, int argc, char *const argv[], + int *argc_nr, struct ethsw_command_def *parsed_cmd); +} keyword[] = { + { + .keyword_name = "help", + .match = &keyword_match_gen, + }, { + .keyword_name = "show", + .match = &keyword_match_gen, + }, { + .keyword_name = "port", + .match = &keyword_match_port + }, { + .keyword_name = "enable", + .match = &keyword_match_gen, + }, { + .keyword_name = "disable", + .match = &keyword_match_gen, + }, +}; + +/* + * Function used by an Ethernet Switch driver to set the functions + * that must be called by the parser when an ethsw command is given + */ +int ethsw_define_functions(const struct ethsw_command_func *cmd_func) +{ + int i; + void **aux_p; + int (*cmd_func_aux)(struct ethsw_command_def *); + + if (!cmd_func->ethsw_name) + return -EINVAL; + + ethsw_name = cmd_func->ethsw_name; + + for (i = 0; i < ARRAY_SIZE(ethsw_cmd_def); i++) { + /* + * get the pointer to the function send by the Ethernet Switch + * driver that corresponds to the proper ethsw command + */ + if (ethsw_cmd_def[i].keyword_function) + continue; + + aux_p = (void *)cmd_func + ethsw_cmd_def[i].cmd_func_offset; + + cmd_func_aux = (int (*)(struct ethsw_command_def *)) *aux_p; + ethsw_cmd_def[i].keyword_function = cmd_func_aux; + } + + return 0; +} + +/* Generic function used to match a keyword only by a string */ +static int keyword_match_gen(enum ethsw_keyword_id key_id, int argc, + char *const argv[], int *argc_nr, + struct ethsw_command_def *parsed_cmd) +{ + if (strcmp(argv[*argc_nr], keyword[key_id].keyword_name) == 0) { + parsed_cmd->cmd_to_keywords[*argc_nr] = key_id; + + return 1; + } + return 0; +} + +/* Function used to match the command's port */ +static int keyword_match_port(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 0; + + if (strict_strtoul(argv[*argc_nr + 1], 10, &val) != -EINVAL) { + parsed_cmd->port = val; + (*argc_nr)++; + parsed_cmd->cmd_to_keywords[*argc_nr] = ethsw_id_port_no; + return 1; + } + + return 0; +} + +/* 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) +{ + int i; + int keyw_opt_matched; + int argc_val_max; + int const *cmd_keyw_p; + int const *cmd_keyw_opt_p; + + /* remember the best match */ + argc_val_max = *argc_val; + + /* + * check if our command's optional keywords match the optional + * keywords of an available command + */ + for (i = 0; i < ARRAY_SIZE(ethsw_cmd_def); i++) { + keyw_opt_matched = 0; + cmd_keyw_p = &parsed_cmd->cmd_to_keywords[keyw_opt_matched]; + cmd_keyw_opt_p = &cmd_opt_def[i].cmd_keyword[keyw_opt_matched]; + + /* + * increase the number of keywords that + * matched with a command + */ + while (keyw_opt_matched + *argc_val < + parsed_cmd->cmd_keywords_nr && + *cmd_keyw_opt_p != ethsw_id_key_end && + *(cmd_keyw_p + *argc_val) == *cmd_keyw_opt_p) { + keyw_opt_matched++; + cmd_keyw_p++; + cmd_keyw_opt_p++; + } + + /* + * if all our optional command's keywords perfectly match an + * optional pattern, then we can move to the next defined + * keywords in our command; remember the one that matched the + * greatest number of keywords + */ + if (keyw_opt_matched + *argc_val <= + parsed_cmd->cmd_keywords_nr && + *cmd_keyw_opt_p == ethsw_id_key_end && + *argc_val + keyw_opt_matched > argc_val_max) + argc_val_max = *argc_val + keyw_opt_matched; + } + + *argc_val = argc_val_max; +} + +/* + * Finds the function to call based on keywords and + * modifies *argc_va to skip them + */ +static void cmd_keywords_check(struct ethsw_command_def *parsed_cmd, + int *argc_val) +{ + int i; + int keyw_matched; + int *cmd_keyw_p; + int *cmd_keyw_def_p; + + /* + * check if our command's keywords match the + * keywords of an available command + */ + for (i = 0; i < ARRAY_SIZE(ethsw_cmd_def); i++) { + keyw_matched = 0; + cmd_keyw_p = &parsed_cmd->cmd_to_keywords[keyw_matched]; + cmd_keyw_def_p = ðsw_cmd_def[i].cmd_keyword[keyw_matched]; + + /* + * increase the number of keywords that + * matched with a command + */ + while (keyw_matched + *argc_val < parsed_cmd->cmd_keywords_nr && + *cmd_keyw_def_p != ethsw_id_key_end && + *(cmd_keyw_p + *argc_val) == *cmd_keyw_def_p) { + keyw_matched++; + cmd_keyw_p++; + cmd_keyw_def_p++; + } + + /* + * if all our command's keywords perfectly match an + * available command, then we get the function we need to call + * to configure the Ethernet Switch + */ + if (keyw_matched && keyw_matched + *argc_val == + parsed_cmd->cmd_keywords_nr && + *cmd_keyw_def_p == ethsw_id_key_end) { + *argc_val += keyw_matched; + parsed_cmd->cmd_function = + ethsw_cmd_def[i].keyword_function; + return; + } + } +} + +/* find all the keywords in the command */ +static int keywords_find(int argc, char * const argv[], + struct ethsw_command_def *parsed_cmd) +{ + int i; + int j; + int argc_val; + int rc = CMD_RET_SUCCESS; + + for (i = 1; i < argc; i++) { + for (j = 0; j < ethsw_id_count; j++) { + if (keyword[j].match(j, argc, argv, &i, parsed_cmd)) + break; + } + } + + /* if there is no keyword match for a word, the command is invalid */ + for (i = 1; i < argc; i++) + if (parsed_cmd->cmd_to_keywords[i] == ethsw_id_key_end) + rc = CMD_RET_USAGE; + + parsed_cmd->cmd_keywords_nr = argc; + argc_val = 1; + + /* get optional parameters first */ + cmd_keywords_opt_check(parsed_cmd, &argc_val); + + if (argc_val == parsed_cmd->cmd_keywords_nr) + return CMD_RET_USAGE; + + /* + * check the keywords and if a match is found, + * get the function to call + */ + cmd_keywords_check(parsed_cmd, &argc_val); + + /* error if not all commands' parameters were matched */ + if (argc_val == parsed_cmd->cmd_keywords_nr) { + if (!parsed_cmd->cmd_function) { + printf("Command not available for: %s\n", ethsw_name); + rc = CMD_RET_FAILURE; + } + } else { + rc = CMD_RET_USAGE; + } + + return rc; +} + +static void command_def_init(struct ethsw_command_def *parsed_cmd) +{ + int i; + + for (i = 0; i < ETHSW_MAX_CMD_PARAMS; i++) + parsed_cmd->cmd_to_keywords[i] = ethsw_id_key_end; + + parsed_cmd->port = ETHSW_CMD_PORT_ALL; + parsed_cmd->cmd_function = NULL; +} + +/* function to interpret commands starting with "ethsw " */ +static int do_ethsw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) +{ + struct ethsw_command_def parsed_cmd; + int rc = CMD_RET_SUCCESS; + + if (argc == 1 || argc >= ETHSW_MAX_CMD_PARAMS) + return CMD_RET_USAGE; + + command_def_init(&parsed_cmd); + + rc = keywords_find(argc, argv, &parsed_cmd); + + if (rc == CMD_RET_SUCCESS) + rc = parsed_cmd.cmd_function(&parsed_cmd); + + return rc; +} + +#define ETHSW_PORT_CONF_HELP "[port ] { enable | disable | show } " \ +"- enable/disable a port; show shows a port's configuration" + +U_BOOT_CMD(ethsw, ETHSW_MAX_CMD_PARAMS, 0, do_ethsw, + "Ethernet l2 switch commands", + ETHSW_PORT_CONF_HELP"\n" +); diff --git a/include/ethsw.h b/include/ethsw.h new file mode 100644 index 0000000..9e80095 --- /dev/null +++ b/include/ethsw.h @@ -0,0 +1,48 @@ +/* + * Copyright 2015 Freescale Semiconductor, Inc. + * + * SPDX-License-Identifier: GPL-2.0+ + * + * Ethernet Switch commands + */ + +#ifndef _CMD_ETHSW_H_ +#define _CMD_ETHSW_H_ + +#define ETHSW_MAX_CMD_PARAMS 20 +#define ETHSW_CMD_PORT_ALL -1 + +/* IDs used to track keywords in a command */ +enum ethsw_keyword_id { + ethsw_id_key_end = -1, + ethsw_id_help, + ethsw_id_show, + ethsw_id_port, + ethsw_id_enable, + ethsw_id_disable, + ethsw_id_count, /* keep last */ +}; + +enum ethsw_keyword_opt_id { + ethsw_id_port_no = ethsw_id_count + 1, + ethsw_id_count_all, /* keep last */ +}; + +struct ethsw_command_def { + int cmd_to_keywords[ETHSW_MAX_CMD_PARAMS]; + int cmd_keywords_nr; + int port; + int (*cmd_function)(struct ethsw_command_def *parsed_cmd); +}; + +/* Structure to be created and initialized by an Ethernet Switch driver */ +struct ethsw_command_func { + const char *ethsw_name; + int (*port_enable)(struct ethsw_command_def *parsed_cmd); + int (*port_disable)(struct ethsw_command_def *parsed_cmd); + int (*port_show)(struct ethsw_command_def *parsed_cmd); +}; + +int ethsw_define_functions(const struct ethsw_command_func *cmd_func); + +#endif /* _CMD_ETHSW_H_ */ -- cgit v0.10.2 From 24a23deb90eea0acf049e226a70d8507394833c5 Mon Sep 17 00:00:00 2001 From: Codrin Ciubotariu Date: Fri, 24 Jul 2015 16:55:28 +0300 Subject: drivers/net/vsc9953: Use the generic Ethernet Switch parser This patch replaces the parser used by VSC9953 L2 Switch driver with the generic one. Also, the config macro that enables the VSC9953 commands has been replaced in all the platforms that use this driver with the config macro that corresponds to the generic parser. Signed-off-by: Codrin Ciubotariu Acked-by: Joe Hershberger Reviewed-by: York Sun diff --git a/drivers/net/vsc9953.c b/drivers/net/vsc9953.c index 59e0fab..bf53535 100644 --- a/drivers/net/vsc9953.c +++ b/drivers/net/vsc9953.c @@ -14,6 +14,7 @@ #include #include #include +#include static struct vsc9953_info vsc9953_l2sw = { .port[0] = VSC9953_PORT_INFO_INITIALIZER(0), @@ -405,6 +406,165 @@ static void vsc9953_port_all_vlan_egress_untagged_set( vsc9953_port_vlan_egr_untag_set(i, mode); } +#ifdef CONFIG_CMD_ETHSW + +/* Enable/disable status of a VSC9953 port */ +static void vsc9953_port_status_set(int port_no, u8 enabled) +{ + struct vsc9953_qsys_reg *l2qsys_reg; + + /* Administrative down */ + if (!vsc9953_l2sw.port[port_no].enabled) + return; + + l2qsys_reg = (struct vsc9953_qsys_reg *)(VSC9953_OFFSET + + VSC9953_QSYS_OFFSET); + + if (enabled) + setbits_le32(&l2qsys_reg->sys.switch_port_mode[port_no], + VSC9953_PORT_ENA); + else + clrbits_le32(&l2qsys_reg->sys.switch_port_mode[port_no], + VSC9953_PORT_ENA); +} + +/* Start autonegotiation for a VSC9953 PHY */ +static void vsc9953_phy_autoneg(int port_no) +{ + if (!vsc9953_l2sw.port[port_no].phydev) + return; + + if (vsc9953_l2sw.port[port_no].phydev->drv->startup( + vsc9953_l2sw.port[port_no].phydev)) + printf("Failed to start PHY for port %d\n", port_no); +} + +/* Print a VSC9953 port's configuration */ +static void vsc9953_port_config_show(int port_no) +{ + int speed; + int duplex; + int link; + u8 enabled; + u32 val; + struct vsc9953_qsys_reg *l2qsys_reg; + + l2qsys_reg = (struct vsc9953_qsys_reg *)(VSC9953_OFFSET + + VSC9953_QSYS_OFFSET); + + val = in_le32(&l2qsys_reg->sys.switch_port_mode[port_no]); + enabled = vsc9953_l2sw.port[port_no].enabled && + (val & VSC9953_PORT_ENA); + + /* internal ports (8 and 9) are fixed */ + if (VSC9953_INTERNAL_PORT_CHECK(port_no)) { + link = 1; + speed = SPEED_2500; + duplex = DUPLEX_FULL; + } else { + if (vsc9953_l2sw.port[port_no].phydev) { + link = vsc9953_l2sw.port[port_no].phydev->link; + speed = vsc9953_l2sw.port[port_no].phydev->speed; + duplex = vsc9953_l2sw.port[port_no].phydev->duplex; + } else { + link = -1; + speed = -1; + duplex = -1; + } + } + + printf("%8d ", port_no); + printf("%8s ", enabled == 1 ? "enabled" : "disabled"); + printf("%8s ", link == 1 ? "up" : "down"); + + switch (speed) { + case SPEED_10: + printf("%8d ", 10); + break; + case SPEED_100: + printf("%8d ", 100); + break; + case SPEED_1000: + printf("%8d ", 1000); + break; + case SPEED_2500: + printf("%8d ", 2500); + break; + case SPEED_10000: + printf("%8d ", 10000); + break; + default: + printf("%8s ", "-"); + } + + printf("%8s\n", duplex == DUPLEX_FULL ? "full" : "half"); +} + +static int vsc9953_port_status_key_func(struct ethsw_command_def *parsed_cmd) +{ + int i; + u8 enabled; + + /* Last keyword should tell us if we should enable/disable the port */ + if (parsed_cmd->cmd_to_keywords[parsed_cmd->cmd_keywords_nr - 1] == + ethsw_id_enable) + enabled = 1; + else if (parsed_cmd->cmd_to_keywords[parsed_cmd->cmd_keywords_nr - 1] == + ethsw_id_disable) + enabled = 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_port_status_set(parsed_cmd->port, enabled); + } else { + for (i = 0; i < VSC9953_MAX_PORTS; i++) + vsc9953_port_status_set(i, enabled); + } + + return CMD_RET_SUCCESS; +} + +static int vsc9953_port_config_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_phy_autoneg(parsed_cmd->port); + printf("%8s %8s %8s %8s %8s\n", + "Port", "Status", "Link", "Speed", + "Duplex"); + vsc9953_port_config_show(parsed_cmd->port); + + } else { + for (i = 0; i < VSC9953_MAX_PORTS; i++) + vsc9953_phy_autoneg(i); + printf("%8s %8s %8s %8s %8s\n", + "Port", "Status", "Link", "Speed", "Duplex"); + for (i = 0; i < VSC9953_MAX_PORTS; i++) + vsc9953_port_config_show(i); + } + + return CMD_RET_SUCCESS; +} + +static struct ethsw_command_func vsc9953_cmd_func = { + .ethsw_name = "L2 Switch VSC9953", + .port_enable = &vsc9953_port_status_key_func, + .port_disable = &vsc9953_port_status_key_func, + .port_show = &vsc9953_port_config_key_func, +}; + +#endif /* CONFIG_CMD_ETHSW */ + /***************************************************************************** At startup, the default configuration would be: - HW learning enabled on all ports; (HW default) @@ -563,190 +723,11 @@ void vsc9953_init(bd_t *bis) vsc9953_default_configuration(); +#ifdef CONFIG_CMD_ETHSW + if (ethsw_define_functions(&vsc9953_cmd_func) < 0) + debug("Unable to use \"ethsw\" commands\n"); +#endif + printf("VSC9953 L2 switch initialized\n"); return; } - -#ifdef CONFIG_VSC9953_CMD -/* Enable/disable status of a VSC9953 port */ -static void vsc9953_port_status_set(int port_no, u8 enabled) -{ - struct vsc9953_qsys_reg *l2qsys_reg; - - /* Administrative down */ - if (!vsc9953_l2sw.port[port_no].enabled) - return; - - l2qsys_reg = (struct vsc9953_qsys_reg *)(VSC9953_OFFSET + - VSC9953_QSYS_OFFSET); - - if (enabled) - setbits_le32(&l2qsys_reg->sys.switch_port_mode[port_no], - VSC9953_PORT_ENA); - else - clrbits_le32(&l2qsys_reg->sys.switch_port_mode[port_no], - VSC9953_PORT_ENA); -} - -/* Set all VSC9953 ports' status */ -static void vsc9953_port_all_status_set(u8 enabled) -{ - int i; - - for (i = 0; i < VSC9953_MAX_PORTS; i++) - vsc9953_port_status_set(i, enabled); -} - -/* Start autonegotiation for a VSC9953 PHY */ -static void vsc9953_phy_autoneg(int port_no) -{ - if (!vsc9953_l2sw.port[port_no].phydev) - return; - - if (vsc9953_l2sw.port[port_no].phydev->drv->startup( - vsc9953_l2sw.port[port_no].phydev)) - printf("Failed to start PHY for port %d\n", port_no); -} - -/* Start autonegotiation for all VSC9953 PHYs */ -static void vsc9953_phy_all_autoneg(void) -{ - int i; - - for (i = 0; i < VSC9953_MAX_PORTS; i++) - vsc9953_phy_autoneg(i); -} - -/* Print a VSC9953 port's configuration */ -static void vsc9953_port_config_show(int port_no) -{ - int speed; - int duplex; - int link; - u8 enabled; - u32 val; - struct vsc9953_qsys_reg *l2qsys_reg; - - l2qsys_reg = (struct vsc9953_qsys_reg *)(VSC9953_OFFSET + - VSC9953_QSYS_OFFSET); - - val = in_le32(&l2qsys_reg->sys.switch_port_mode[port_no]); - enabled = vsc9953_l2sw.port[port_no].enabled && - (val & VSC9953_PORT_ENA); - - /* internal ports (8 and 9) are fixed */ - if (VSC9953_INTERNAL_PORT_CHECK(port_no)) { - link = 1; - speed = SPEED_2500; - duplex = DUPLEX_FULL; - } else { - if (vsc9953_l2sw.port[port_no].phydev) { - link = vsc9953_l2sw.port[port_no].phydev->link; - speed = vsc9953_l2sw.port[port_no].phydev->speed; - duplex = vsc9953_l2sw.port[port_no].phydev->duplex; - } else { - link = -1; - speed = -1; - duplex = -1; - } - } - - printf("%8d ", port_no); - printf("%8s ", enabled == 1 ? "enabled" : "disabled"); - printf("%8s ", link == 1 ? "up" : "down"); - - switch (speed) { - case SPEED_10: - printf("%8d ", 10); - break; - case SPEED_100: - printf("%8d ", 100); - break; - case SPEED_1000: - printf("%8d ", 1000); - break; - case SPEED_2500: - printf("%8d ", 2500); - break; - case SPEED_10000: - printf("%8d ", 10000); - break; - default: - printf("%8s ", "-"); - } - - printf("%8s\n", duplex == DUPLEX_FULL ? "full" : "half"); -} - -/* Print VSC9953 ports' configuration */ -static void vsc9953_port_all_config_show(void) -{ - int i; - - for (i = 0; i < VSC9953_MAX_PORTS; i++) - vsc9953_port_config_show(i); -} - -/* function to interpret commands starting with "ethsw " */ -static int do_ethsw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) -{ - u8 enable; - u32 port; - - if (argc < 4) - return -1; - - if (strcmp(argv[1], "port")) - return -1; - - if (!strcmp(argv[3], "show")) { - if (!strcmp(argv[2], "all")) { - vsc9953_phy_all_autoneg(); - printf("%8s %8s %8s %8s %8s\n", - "Port", "Status", "Link", "Speed", - "Duplex"); - vsc9953_port_all_config_show(); - return 0; - } else { - port = simple_strtoul(argv[2], NULL, 10); - if (!VSC9953_PORT_CHECK(port)) - return -1; - vsc9953_phy_autoneg(port); - printf("%8s %8s %8s %8s %8s\n", - "Port", "Status", "Link", "Speed", - "Duplex"); - vsc9953_port_config_show(port); - return 0; - } - } else if (!strcmp(argv[3], "enable")) { - enable = 1; - } else if (!strcmp(argv[3], "disable")) { - enable = 0; - } else { - return -1; - } - - if (!strcmp(argv[2], "all")) { - vsc9953_port_all_status_set(enable); - return 0; - } else { - port = simple_strtoul(argv[2], NULL, 10); - if (!VSC9953_PORT_CHECK(port)) - return -1; - vsc9953_port_status_set(port, enable); - return 0; - } - - return -1; -} - -U_BOOT_CMD(ethsw, 5, 0, do_ethsw, - "vsc9953 l2 switch commands", - "port enable|disable\n" - " - enable/disable an l2 switch port\n" - " port_no=0..9; use \"all\" for all ports\n" - "ethsw port show\n" - " - show an l2 switch port's configuration\n" - " port_no=0..9; use \"all\" for all ports\n" -); -#endif /* CONFIG_VSC9953_CMD */ diff --git a/include/configs/T104xRDB.h b/include/configs/T104xRDB.h index cd5b3e2..5b61b56 100644 --- a/include/configs/T104xRDB.h +++ b/include/configs/T104xRDB.h @@ -759,7 +759,7 @@ $(SRCTREE)/board/freescale/t104xrdb/t1042d4_rcw.cfg /* Enable VSC9953 L2 Switch driver on T1040 SoC */ #if defined(CONFIG_T1040RDB) || defined(CONFIG_T1040D4RDB) #define CONFIG_VSC9953 -#define CONFIG_VSC9953_CMD +#define CONFIG_CMD_ETHSW #ifdef CONFIG_T1040RDB #define CONFIG_SYS_FM1_QSGMII11_PHY_ADDR 0x04 #define CONFIG_SYS_FM1_QSGMII21_PHY_ADDR 0x08 -- cgit v0.10.2 From 86719f0cd55bc13186798217b08fa6a048eda27c Mon Sep 17 00:00:00 2001 From: Codrin Ciubotariu Date: Fri, 24 Jul 2015 16:55:29 +0300 Subject: drivers/net/vsc9953: Add command to show/clear port counters The new added command: ethsw [port ] statistics { [help] | [clear] } will print counters like the number of Rx/Tx frames, number of Rx/Tx bytes, number of Rx/Tx unicast frames, etc. This patch also adds this commnd in the genereric ethsw parser from cmd_ethsw.c 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 9a7ae74..8c54a3d 100644 --- a/common/cmd_ethsw.c +++ b/common/cmd_ethsw.c @@ -13,6 +13,16 @@ static const char *ethsw_name; +#define ETHSW_PORT_STATS_HELP "ethsw [port ] statistics " \ +"{ [help] | [clear] } - show an l2 switch port's statistics" + +static int ethsw_port_stats_help_key_func(struct ethsw_command_def *parsed_cmd) +{ + printf(ETHSW_PORT_STATS_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; @@ -42,6 +52,31 @@ static struct keywords_to_function { .cmd_func_offset = offsetof(struct ethsw_command_func, port_show), .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_statistics, + ethsw_id_help, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_port_stats_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_statistics, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_stats), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_statistics, + ethsw_id_clear, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_stats_clear), + .keyword_function = NULL, }, }; @@ -88,6 +123,12 @@ struct keyword_def { }, { .keyword_name = "disable", .match = &keyword_match_gen, + }, { + .keyword_name = "statistics", + .match = &keyword_match_gen, + }, { + .keyword_name = "clear", + .match = &keyword_match_gen, }, }; @@ -344,4 +385,5 @@ static int do_ethsw(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) 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" ); diff --git a/drivers/net/vsc9953.c b/drivers/net/vsc9953.c index bf53535..1dddb44 100644 --- a/drivers/net/vsc9953.c +++ b/drivers/net/vsc9953.c @@ -500,6 +500,223 @@ static void vsc9953_port_config_show(int port_no) printf("%8s\n", duplex == DUPLEX_FULL ? "full" : "half"); } +/* Show VSC9953 ports' statistics */ +static void vsc9953_port_statistics_show(int port_no) +{ + u32 rx_val; + u32 tx_val; + struct vsc9953_system_reg *l2sys_reg; + + /* Administrative down */ + if (!vsc9953_l2sw.port[port_no].enabled) { + printf("Port %d is administrative down\n", port_no); + return; + } + + l2sys_reg = (struct vsc9953_system_reg *)(VSC9953_OFFSET + + VSC9953_SYS_OFFSET); + + printf("Statistics for L2 Switch port %d:\n", port_no); + + /* Set counter view for our port */ + out_le32(&l2sys_reg->sys.stat_cfg, port_no); + +#define VSC9953_STATS_PRINTF "%-15s %10u" + + /* Get number of Rx and Tx frames */ + rx_val = in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_short) + + in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_frag) + + in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_jabber) + + in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_long) + + in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_sz_64) + + in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_sz_65_127) + + in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_sz_128_255) + + in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_sz_256_511) + + in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_sz_512_1023) + + in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_sz_1024_1526) + + in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_sz_jumbo); + tx_val = in_le32(&l2sys_reg->stat.tx_cntrs.c_tx_sz_64) + + in_le32(&l2sys_reg->stat.tx_cntrs.c_tx_sz_65_127) + + in_le32(&l2sys_reg->stat.tx_cntrs.c_tx_sz_128_255) + + in_le32(&l2sys_reg->stat.tx_cntrs.c_tx_sz_256_511) + + in_le32(&l2sys_reg->stat.tx_cntrs.c_tx_sz_512_1023) + + in_le32(&l2sys_reg->stat.tx_cntrs.c_tx_sz_1024_1526) + + in_le32(&l2sys_reg->stat.tx_cntrs.c_tx_sz_jumbo); + printf(VSC9953_STATS_PRINTF"\t\t"VSC9953_STATS_PRINTF"\n", + "Rx frames:", rx_val, "Tx frames:", tx_val); + + /* Get number of Rx and Tx bytes */ + rx_val = in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_oct); + tx_val = in_le32(&l2sys_reg->stat.tx_cntrs.c_tx_oct); + printf(VSC9953_STATS_PRINTF"\t\t"VSC9953_STATS_PRINTF"\n", + "Rx bytes:", rx_val, "Tx bytes:", tx_val); + + /* Get number of Rx frames received ok and Tx frames sent ok */ + rx_val = in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_yellow_prio_0) + + in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_yellow_prio_1) + + in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_yellow_prio_2) + + in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_yellow_prio_3) + + in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_yellow_prio_4) + + in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_yellow_prio_5) + + in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_yellow_prio_6) + + in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_yellow_prio_7) + + in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_green_prio_0) + + in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_green_prio_1) + + in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_green_prio_2) + + in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_green_prio_3) + + in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_green_prio_4) + + in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_green_prio_5) + + in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_green_prio_6) + + in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_green_prio_7); + tx_val = in_le32(&l2sys_reg->stat.tx_cntrs.c_tx_sz_64) + + in_le32(&l2sys_reg->stat.tx_cntrs.c_tx_sz_65_127) + + in_le32(&l2sys_reg->stat.tx_cntrs.c_tx_sz_128_255) + + in_le32(&l2sys_reg->stat.tx_cntrs.c_tx_sz_256_511) + + in_le32(&l2sys_reg->stat.tx_cntrs.c_tx_sz_512_1023) + + in_le32(&l2sys_reg->stat.tx_cntrs.c_tx_sz_1024_1526) + + in_le32(&l2sys_reg->stat.tx_cntrs.c_tx_sz_jumbo); + printf(VSC9953_STATS_PRINTF"\t\t"VSC9953_STATS_PRINTF"\n", + "Rx frames ok:", rx_val, "Tx frames ok:", tx_val); + + /* Get number of Rx and Tx unicast frames */ + rx_val = in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_uc); + tx_val = in_le32(&l2sys_reg->stat.tx_cntrs.c_tx_uc); + printf(VSC9953_STATS_PRINTF"\t\t"VSC9953_STATS_PRINTF"\n", + "Rx unicast:", rx_val, "Tx unicast:", tx_val); + + /* Get number of Rx and Tx broadcast frames */ + rx_val = in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_bc); + tx_val = in_le32(&l2sys_reg->stat.tx_cntrs.c_tx_bc); + printf(VSC9953_STATS_PRINTF"\t\t"VSC9953_STATS_PRINTF"\n", + "Rx broadcast:", rx_val, "Tx broadcast:", tx_val); + + /* Get number of Rx and Tx frames of 64B */ + rx_val = in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_sz_64); + tx_val = in_le32(&l2sys_reg->stat.tx_cntrs.c_tx_sz_64); + printf(VSC9953_STATS_PRINTF"\t\t"VSC9953_STATS_PRINTF"\n", + "Rx 64B:", rx_val, "Tx 64B:", tx_val); + + /* Get number of Rx and Tx frames with sizes between 65B and 127B */ + rx_val = in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_sz_65_127); + tx_val = in_le32(&l2sys_reg->stat.tx_cntrs.c_tx_sz_65_127); + printf(VSC9953_STATS_PRINTF"\t\t"VSC9953_STATS_PRINTF"\n", + "Rx 65B-127B:", rx_val, "Tx 65B-127B:", tx_val); + + /* Get number of Rx and Tx frames with sizes between 128B and 255B */ + rx_val = in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_sz_128_255); + tx_val = in_le32(&l2sys_reg->stat.tx_cntrs.c_tx_sz_128_255); + printf(VSC9953_STATS_PRINTF"\t\t"VSC9953_STATS_PRINTF"\n", + "Rx 128B-255B:", rx_val, "Tx 128B-255B:", tx_val); + + /* Get number of Rx and Tx frames with sizes between 256B and 511B */ + rx_val = in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_sz_256_511); + tx_val = in_le32(&l2sys_reg->stat.tx_cntrs.c_tx_sz_256_511); + printf(VSC9953_STATS_PRINTF"\t\t"VSC9953_STATS_PRINTF"\n", + "Rx 256B-511B:", rx_val, "Tx 256B-511B:", tx_val); + + /* Get number of Rx and Tx frames with sizes between 512B and 1023B */ + rx_val = in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_sz_512_1023); + tx_val = in_le32(&l2sys_reg->stat.tx_cntrs.c_tx_sz_512_1023); + printf(VSC9953_STATS_PRINTF"\t\t"VSC9953_STATS_PRINTF"\n", + "Rx 512B-1023B:", rx_val, "Tx 512B-1023B:", tx_val); + + /* Get number of Rx and Tx frames with sizes between 1024B and 1526B */ + rx_val = in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_sz_1024_1526); + tx_val = in_le32(&l2sys_reg->stat.tx_cntrs.c_tx_sz_1024_1526); + printf(VSC9953_STATS_PRINTF"\t\t"VSC9953_STATS_PRINTF"\n", + "Rx 1024B-1526B:", rx_val, "Tx 1024B-1526B:", tx_val); + + /* Get number of Rx and Tx jumbo frames */ + rx_val = in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_sz_jumbo); + tx_val = in_le32(&l2sys_reg->stat.tx_cntrs.c_tx_sz_jumbo); + printf(VSC9953_STATS_PRINTF"\t\t"VSC9953_STATS_PRINTF"\n", + "Rx jumbo:", rx_val, "Tx jumbo:", tx_val); + + /* Get number of Rx and Tx dropped frames */ + rx_val = in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_cat_drop) + + in_le32(&l2sys_reg->stat.drop_cntrs.c_dr_tail) + + in_le32(&l2sys_reg->stat.drop_cntrs.c_dr_yellow_prio_0) + + in_le32(&l2sys_reg->stat.drop_cntrs.c_dr_yellow_prio_1) + + in_le32(&l2sys_reg->stat.drop_cntrs.c_dr_yellow_prio_2) + + in_le32(&l2sys_reg->stat.drop_cntrs.c_dr_yellow_prio_3) + + in_le32(&l2sys_reg->stat.drop_cntrs.c_dr_yellow_prio_4) + + in_le32(&l2sys_reg->stat.drop_cntrs.c_dr_yellow_prio_5) + + in_le32(&l2sys_reg->stat.drop_cntrs.c_dr_yellow_prio_6) + + in_le32(&l2sys_reg->stat.drop_cntrs.c_dr_yellow_prio_7) + + in_le32(&l2sys_reg->stat.drop_cntrs.c_dr_green_prio_0) + + in_le32(&l2sys_reg->stat.drop_cntrs.c_dr_green_prio_1) + + in_le32(&l2sys_reg->stat.drop_cntrs.c_dr_green_prio_2) + + in_le32(&l2sys_reg->stat.drop_cntrs.c_dr_green_prio_3) + + in_le32(&l2sys_reg->stat.drop_cntrs.c_dr_green_prio_4) + + in_le32(&l2sys_reg->stat.drop_cntrs.c_dr_green_prio_5) + + in_le32(&l2sys_reg->stat.drop_cntrs.c_dr_green_prio_6) + + in_le32(&l2sys_reg->stat.drop_cntrs.c_dr_green_prio_7); + tx_val = in_le32(&l2sys_reg->stat.tx_cntrs.c_tx_drop) + + in_le32(&l2sys_reg->stat.tx_cntrs.c_tx_aged); + printf(VSC9953_STATS_PRINTF"\t\t"VSC9953_STATS_PRINTF"\n", + "Rx drops:", rx_val, "Tx drops:", tx_val); + + /* + * Get number of Rx frames with CRC or alignment errors + * and number of detected Tx collisions + */ + rx_val = in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_crc); + tx_val = in_le32(&l2sys_reg->stat.tx_cntrs.c_tx_col); + printf(VSC9953_STATS_PRINTF"\t\t"VSC9953_STATS_PRINTF"\n", + "Rx CRC&align:", rx_val, "Tx coll:", tx_val); + + /* + * Get number of Rx undersized frames and + * number of Tx aged frames + */ + rx_val = in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_short); + tx_val = in_le32(&l2sys_reg->stat.tx_cntrs.c_tx_aged); + printf(VSC9953_STATS_PRINTF"\t\t"VSC9953_STATS_PRINTF"\n", + "Rx undersize:", rx_val, "Tx aged:", tx_val); + + /* Get number of Rx oversized frames */ + rx_val = in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_long); + printf(VSC9953_STATS_PRINTF"\n", "Rx oversized:", rx_val); + + /* Get number of Rx fragmented frames */ + rx_val = in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_frag); + printf(VSC9953_STATS_PRINTF"\n", "Rx fragments:", rx_val); + + /* Get number of Rx jabber errors */ + rx_val = in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_jabber); + printf(VSC9953_STATS_PRINTF"\n", "Rx jabbers:", rx_val); + + /* + * Get number of Rx frames filtered due to classification rules or + * no destination ports + */ + rx_val = in_le32(&l2sys_reg->stat.rx_cntrs.c_rx_cat_drop) + + in_le32(&l2sys_reg->stat.drop_cntrs.c_dr_local); + printf(VSC9953_STATS_PRINTF"\n", "Rx filtered:", rx_val); + + printf("\n"); +} + +/* Clear statistics for a VSC9953 port */ +static void vsc9953_port_statistics_clear(int port_no) +{ + struct vsc9953_system_reg *l2sys_reg; + + /* Administrative down */ + if (!vsc9953_l2sw.port[port_no].enabled) { + printf("Port %d is administrative down\n", port_no); + return; + } + + l2sys_reg = (struct vsc9953_system_reg *)(VSC9953_OFFSET + + VSC9953_SYS_OFFSET); + + /* Clear all counter groups for our ports */ + out_le32(&l2sys_reg->sys.stat_cfg, port_no | + VSC9953_STAT_CLEAR_RX | VSC9953_STAT_CLEAR_TX | + VSC9953_STAT_CLEAR_DR); +} + static int vsc9953_port_status_key_func(struct ethsw_command_def *parsed_cmd) { int i; @@ -556,11 +773,50 @@ static int vsc9953_port_config_key_func(struct ethsw_command_def *parsed_cmd) return CMD_RET_SUCCESS; } +static int vsc9953_port_stats_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_port_statistics_show(parsed_cmd->port); + } else { + for (i = 0; i < VSC9953_MAX_PORTS; i++) + vsc9953_port_statistics_show(i); + } + + return CMD_RET_SUCCESS; +} + +static int vsc9953_port_stats_clear_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_port_statistics_clear(parsed_cmd->port); + } else { + for (i = 0; i < VSC9953_MAX_PORTS; i++) + vsc9953_port_statistics_clear(i); + } + + return CMD_RET_SUCCESS; +} + static struct ethsw_command_func vsc9953_cmd_func = { .ethsw_name = "L2 Switch VSC9953", .port_enable = &vsc9953_port_status_key_func, .port_disable = &vsc9953_port_status_key_func, .port_show = &vsc9953_port_config_key_func, + .port_stats = &vsc9953_port_stats_key_func, + .port_stats_clear = &vsc9953_port_stats_clear_key_func, }; #endif /* CONFIG_CMD_ETHSW */ diff --git a/include/ethsw.h b/include/ethsw.h index 9e80095..8f1c414 100644 --- a/include/ethsw.h +++ b/include/ethsw.h @@ -20,6 +20,8 @@ enum ethsw_keyword_id { ethsw_id_port, ethsw_id_enable, ethsw_id_disable, + ethsw_id_statistics, + ethsw_id_clear, ethsw_id_count, /* keep last */ }; @@ -41,6 +43,8 @@ struct ethsw_command_func { int (*port_enable)(struct ethsw_command_def *parsed_cmd); int (*port_disable)(struct ethsw_command_def *parsed_cmd); 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 ethsw_define_functions(const struct ethsw_command_func *cmd_func); diff --git a/include/vsc9953.h b/include/vsc9953.h index bb9e22d..83c4c89 100644 --- a/include/vsc9953.h +++ b/include/vsc9953.h @@ -77,6 +77,11 @@ /* Macros for vsc9953_sys_pause_cfgtot_tail_drop_lvl register */ #define VSC9953_TOT_TAIL_DROP_LVL 0x000003ff +/* Macros for vsc9953_sys_sys.stat_cfg register */ +#define VSC9953_STAT_CLEAR_RX 0x00000400 +#define VSC9953_STAT_CLEAR_TX 0x00000800 +#define VSC9953_STAT_CLEAR_DR 0x00001000 + /* Macros for vsc9953_vcap_core_cfg.vcap_mv_cfg register */ #define VSC9953_VCAP_MV_CFG 0x0000ffff #define VSC9953_VCAP_UPDATE_CTRL 0x01000004 @@ -332,10 +337,115 @@ struct vsc9953_qsys_reg { /* VSC9953 SYS structure */ +struct vsc9953_rx_cntrs { + u32 c_rx_oct; + u32 c_rx_uc; + u32 c_rx_mc; + u32 c_rx_bc; + u32 c_rx_short; + u32 c_rx_frag; + u32 c_rx_jabber; + u32 c_rx_crc; + u32 c_rx_symbol_err; + u32 c_rx_sz_64; + u32 c_rx_sz_65_127; + u32 c_rx_sz_128_255; + u32 c_rx_sz_256_511; + u32 c_rx_sz_512_1023; + u32 c_rx_sz_1024_1526; + u32 c_rx_sz_jumbo; + u32 c_rx_pause; + u32 c_rx_control; + u32 c_rx_long; + u32 c_rx_cat_drop; + u32 c_rx_red_prio_0; + u32 c_rx_red_prio_1; + u32 c_rx_red_prio_2; + u32 c_rx_red_prio_3; + u32 c_rx_red_prio_4; + u32 c_rx_red_prio_5; + u32 c_rx_red_prio_6; + u32 c_rx_red_prio_7; + u32 c_rx_yellow_prio_0; + u32 c_rx_yellow_prio_1; + u32 c_rx_yellow_prio_2; + u32 c_rx_yellow_prio_3; + u32 c_rx_yellow_prio_4; + u32 c_rx_yellow_prio_5; + u32 c_rx_yellow_prio_6; + u32 c_rx_yellow_prio_7; + u32 c_rx_green_prio_0; + u32 c_rx_green_prio_1; + u32 c_rx_green_prio_2; + u32 c_rx_green_prio_3; + u32 c_rx_green_prio_4; + u32 c_rx_green_prio_5; + u32 c_rx_green_prio_6; + u32 c_rx_green_prio_7; + u32 reserved[20]; +}; + +struct vsc9953_tx_cntrs { + u32 c_tx_oct; + u32 c_tx_uc; + u32 c_tx_mc; + u32 c_tx_bc; + u32 c_tx_col; + u32 c_tx_drop; + u32 c_tx_pause; + u32 c_tx_sz_64; + u32 c_tx_sz_65_127; + u32 c_tx_sz_128_255; + u32 c_tx_sz_256_511; + u32 c_tx_sz_512_1023; + u32 c_tx_sz_1024_1526; + u32 c_tx_sz_jumbo; + u32 c_tx_yellow_prio_0; + u32 c_tx_yellow_prio_1; + u32 c_tx_yellow_prio_2; + u32 c_tx_yellow_prio_3; + u32 c_tx_yellow_prio_4; + u32 c_tx_yellow_prio_5; + u32 c_tx_yellow_prio_6; + u32 c_tx_yellow_prio_7; + u32 c_tx_green_prio_0; + u32 c_tx_green_prio_1; + u32 c_tx_green_prio_2; + u32 c_tx_green_prio_3; + u32 c_tx_green_prio_4; + u32 c_tx_green_prio_5; + u32 c_tx_green_prio_6; + u32 c_tx_green_prio_7; + u32 c_tx_aged; + u32 reserved[33]; +}; + +struct vsc9953_drop_cntrs { + u32 c_dr_local; + u32 c_dr_tail; + u32 c_dr_yellow_prio_0; + u32 c_dr_yellow_prio_1; + u32 c_dr_yellow_prio_2; + u32 c_dr_yellow_prio_3; + u32 c_dr_yellow_prio_4; + u32 c_dr_yellow_prio_5; + u32 c_dr_yellow_prio_6; + u32 c_dr_yellow_prio_7; + u32 c_dr_green_prio_0; + u32 c_dr_green_prio_1; + u32 c_dr_green_prio_2; + u32 c_dr_green_prio_3; + u32 c_dr_green_prio_4; + u32 c_dr_green_prio_5; + u32 c_dr_green_prio_6; + u32 c_dr_green_prio_7; + u32 reserved[46]; +}; + struct vsc9953_sys_stat { - u32 rx_cntrs[64]; - u32 tx_cntrs[64]; - u32 drop_cntrs[64]; + struct vsc9953_rx_cntrs rx_cntrs; + struct vsc9953_tx_cntrs tx_cntrs; + struct vsc9953_drop_cntrs drop_cntrs; u32 reserved1[6]; }; -- cgit v0.10.2 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 From 0118e83ba4e1811515dc944a826758cad8a394b7 Mon Sep 17 00:00:00 2001 From: Codrin Ciubotariu Date: Wed, 9 Sep 2015 18:00:51 +0300 Subject: common/env_flags.c: Add function to validate a MAC address The code that checks if a string has the format of a MAC address has been moved to a separate function called eth_validate_ethaddr_str(). This has been done to allow other components (such as vsc9953 driver) to validate a MAC address. Signed-off-by: Codrin Ciubotariu Acked-by: Joe Hershberger Reviewed-by: York Sun diff --git a/common/env_flags.c b/common/env_flags.c index 5189f5b..e682d85 100644 --- a/common/env_flags.c +++ b/common/env_flags.c @@ -187,6 +187,31 @@ static void skip_num(int hex, const char *value, const char **end, *end = value; } +#ifdef CONFIG_CMD_NET +int eth_validate_ethaddr_str(const char *addr) +{ + const char *end; + const char *cur; + int i; + + cur = addr; + for (i = 0; i < 6; i++) { + skip_num(1, cur, &end, 2); + if (cur == end) + return -1; + if (cur + 2 == end && is_hex_prefix(cur)) + return -1; + if (i != 5 && *end != ':') + return -1; + if (i == 5 && *end != '\0') + return -1; + cur = end + 1; + } + + return 0; +} +#endif + /* * Based on the declared type enum, validate that the value string complies * with that format @@ -239,19 +264,8 @@ static int _env_flags_validate_type(const char *value, } break; case env_flags_vartype_macaddr: - cur = value; - for (i = 0; i < 6; i++) { - skip_num(1, cur, &end, 2); - if (cur == end) - return -1; - if (cur + 2 == end && is_hex_prefix(cur)) - return -1; - if (i != 5 && *end != ':') - return -1; - if (i == 5 && *end != '\0') - return -1; - cur = end + 1; - } + if (eth_validate_ethaddr_str(value)) + return -1; break; #endif case env_flags_vartype_end: diff --git a/include/env_flags.h b/include/env_flags.h index 2d2de88..8823fb9 100644 --- a/include/env_flags.h +++ b/include/env_flags.h @@ -109,6 +109,13 @@ enum env_flags_varaccess env_flags_parse_varaccess(const char *flags); */ enum env_flags_varaccess env_flags_parse_varaccess_from_binflags(int binflags); +#ifdef CONFIG_CMD_NET +/* + * Check if a string has the format of an Ethernet MAC address + */ +int eth_validate_ethaddr_str(const char *addr); +#endif + #ifdef USE_HOSTCC /* * Look up the type of a variable directly from the .flags var. -- cgit v0.10.2 From 22449858f8eae3f03399f76b4a982dd2f0d4df00 Mon Sep 17 00:00:00 2001 From: Codrin Ciubotariu Date: Wed, 9 Sep 2015 18:00:52 +0300 Subject: drivers/net/vsc9953: Add commands to manipulate the FDB for VSC9953 The new command: ethsw [port ] [vlan ] fdb { [help] | show | flush | { add | del } } Can be used to add and delete FDB entries. Also, the command can be used to show entries from the FDB tables. When used with [port ] and [vlan ], only the matching the FDB entries can be seen or flushed. The command has also been added to the generic ethsw parser from cmd_ethsw.c. 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 c1c33d1..9ba680a 100644 --- a/common/cmd_ethsw.c +++ b/common/cmd_ethsw.c @@ -9,6 +9,7 @@ #include #include #include +#include #include static const char *ethsw_name; @@ -34,6 +35,18 @@ static int ethsw_learn_help_key_func(struct ethsw_command_def *parsed_cmd) return CMD_RET_SUCCESS; } +#define ETHSW_FDB_HELP "ethsw [port ] [vlan ] fdb " \ +"{ [help] | show | flush | { add | del } } " \ +"- Add/delete a mac entry in FDB; use show to see FDB entries; " \ +"if vlan is missing, VID 1 will be used" + +static int ethsw_fdb_help_key_func(struct ethsw_command_def *parsed_cmd) +{ + printf(ETHSW_FDB_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; @@ -130,6 +143,59 @@ static struct keywords_to_function { .cmd_func_offset = offsetof(struct ethsw_command_func, port_learn), .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_fdb, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_fdb_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_fdb, + ethsw_id_help, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_fdb_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_fdb, + ethsw_id_show, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + fdb_show), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_fdb, + ethsw_id_flush, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + fdb_flush), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_fdb, + ethsw_id_add, + ethsw_id_add_del_mac, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + fdb_entry_add), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_fdb, + ethsw_id_del, + ethsw_id_add_del_mac, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + fdb_entry_del), + .keyword_function = NULL, }, }; @@ -142,6 +208,20 @@ struct keywords_optional { ethsw_id_port_no, ethsw_id_key_end, }, + }, { + .cmd_keyword = { + ethsw_id_vlan, + ethsw_id_vlan_no, + ethsw_id_key_end, + }, + }, { + .cmd_keyword = { + ethsw_id_port, + ethsw_id_port_no, + ethsw_id_vlan, + ethsw_id_vlan_no, + ethsw_id_key_end, + }, }, }; @@ -151,6 +231,12 @@ static int keyword_match_gen(enum ethsw_keyword_id key_id, int argc, char static int keyword_match_port(enum ethsw_keyword_id key_id, int argc, char *const argv[], int *argc_nr, struct ethsw_command_def *parsed_cmd); +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_mac_addr(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; @@ -188,6 +274,21 @@ struct keyword_def { }, { .keyword_name = "auto", .match = &keyword_match_gen, + }, { + .keyword_name = "vlan", + .match = &keyword_match_vlan, + }, { + .keyword_name = "fdb", + .match = &keyword_match_gen, + }, { + .keyword_name = "add", + .match = &keyword_match_mac_addr, + }, { + .keyword_name = "del", + .match = &keyword_match_mac_addr, + }, { + .keyword_name = "flush", + .match = &keyword_match_gen, }, }; @@ -259,6 +360,78 @@ static int keyword_match_port(enum ethsw_keyword_id key_id, int argc, return 0; } +/* Function used to match the command's vlan */ +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) +{ + unsigned long val; + int aux; + + if (!keyword_match_gen(key_id, argc, argv, argc_nr, parsed_cmd)) + return 0; + + if (*argc_nr + 1 >= argc) + return 0; + + 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_vlan_no; + return 1; + } + + aux = *argc_nr + 1; + + if (keyword_match_gen(ethsw_id_add, argc, argv, &aux, parsed_cmd)) + parsed_cmd->cmd_to_keywords[*argc_nr + 1] = ethsw_id_add; + else if (keyword_match_gen(ethsw_id_del, argc, argv, &aux, parsed_cmd)) + parsed_cmd->cmd_to_keywords[*argc_nr + 1] = ethsw_id_del; + else + return 0; + + if (*argc_nr + 2 >= argc) + return 0; + + if (strict_strtoul(argv[*argc_nr + 2], 10, &val) != -EINVAL) { + parsed_cmd->vid = val; + (*argc_nr) += 2; + parsed_cmd->cmd_to_keywords[*argc_nr] = ethsw_id_add_del_no; + return 1; + } + + return 0; +} + +/* 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, + struct ethsw_command_def *parsed_cmd) +{ + if (!keyword_match_gen(key_id, argc, argv, argc_nr, parsed_cmd)) + return 0; + + if ((*argc_nr + 1 >= argc) || + !is_broadcast_ethaddr(parsed_cmd->ethaddr)) + return 1; + + if (eth_validate_ethaddr_str(argv[*argc_nr + 1])) { + printf("Invalid MAC address: %s\n", argv[*argc_nr + 1]); + return 0; + } + + eth_parse_enetaddr(argv[*argc_nr + 1], parsed_cmd->ethaddr); + + if (is_broadcast_ethaddr(parsed_cmd->ethaddr)) { + memset(parsed_cmd->ethaddr, 0xFF, sizeof(parsed_cmd->ethaddr)); + return 0; + } + + parsed_cmd->cmd_to_keywords[*argc_nr + 1] = ethsw_id_add_del_mac; + + 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) @@ -416,7 +589,11 @@ static void command_def_init(struct ethsw_command_def *parsed_cmd) parsed_cmd->cmd_to_keywords[i] = ethsw_id_key_end; parsed_cmd->port = ETHSW_CMD_PORT_ALL; + parsed_cmd->vid = ETHSW_CMD_VLAN_ALL; parsed_cmd->cmd_function = NULL; + + /* We initialize the MAC address with the Broadcast address */ + memset(parsed_cmd->ethaddr, 0xff, sizeof(parsed_cmd->ethaddr)); } /* function to interpret commands starting with "ethsw " */ @@ -446,4 +623,5 @@ U_BOOT_CMD(ethsw, ETHSW_MAX_CMD_PARAMS, 0, do_ethsw, ETHSW_PORT_CONF_HELP"\n" ETHSW_PORT_STATS_HELP"\n" ETHSW_LEARN_HELP"\n" + ETHSW_FDB_HELP"\n" ); diff --git a/drivers/net/vsc9953.c b/drivers/net/vsc9953.c index 21b14e6..071906c 100644 --- a/drivers/net/vsc9953.c +++ b/drivers/net/vsc9953.c @@ -783,6 +783,389 @@ static int vsc9953_port_learn_mode_get(int port_no, enum port_learn_mode *mode) return 0; } +/* wait for FDB to become available */ +static int vsc9953_mac_table_poll_idle(void) +{ + struct vsc9953_analyzer *l2ana_reg; + u32 timeout; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + timeout = 50000; + while (((in_le32(&l2ana_reg->ana_tables.mac_access) & + VSC9953_MAC_CMD_MASK) != + VSC9953_MAC_CMD_IDLE) && --timeout) + udelay(1); + + return timeout ? 0 : -EBUSY; +} + +/* enum describing available commands for the MAC table */ +enum mac_table_cmd { + MAC_TABLE_READ, + MAC_TABLE_LOOKUP, + MAC_TABLE_WRITE, + MAC_TABLE_LEARN, + MAC_TABLE_FORGET, + MAC_TABLE_GET_NEXT, + MAC_TABLE_AGE, +}; + +/* Issues a command to the FDB table */ +static int vsc9953_mac_table_cmd(enum mac_table_cmd cmd) +{ + struct vsc9953_analyzer *l2ana_reg; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + switch (cmd) { + case MAC_TABLE_READ: + clrsetbits_le32(&l2ana_reg->ana_tables.mac_access, + VSC9953_MAC_CMD_MASK | VSC9953_MAC_CMD_VALID, + VSC9953_MAC_CMD_READ); + break; + case MAC_TABLE_LOOKUP: + clrsetbits_le32(&l2ana_reg->ana_tables.mac_access, + VSC9953_MAC_CMD_MASK, VSC9953_MAC_CMD_READ | + VSC9953_MAC_CMD_VALID); + break; + case MAC_TABLE_WRITE: + clrsetbits_le32(&l2ana_reg->ana_tables.mac_access, + VSC9953_MAC_CMD_MASK | + VSC9953_MAC_ENTRYTYPE_MASK, + VSC9953_MAC_CMD_WRITE | + VSC9953_MAC_ENTRYTYPE_LOCKED); + break; + case MAC_TABLE_LEARN: + clrsetbits_le32(&l2ana_reg->ana_tables.mac_access, + VSC9953_MAC_CMD_MASK | + VSC9953_MAC_ENTRYTYPE_MASK, + VSC9953_MAC_CMD_LEARN | + VSC9953_MAC_ENTRYTYPE_LOCKED | + VSC9953_MAC_CMD_VALID); + break; + case MAC_TABLE_FORGET: + clrsetbits_le32(&l2ana_reg->ana_tables.mac_access, + VSC9953_MAC_CMD_MASK | + VSC9953_MAC_ENTRYTYPE_MASK, + VSC9953_MAC_CMD_FORGET); + break; + case MAC_TABLE_GET_NEXT: + clrsetbits_le32(&l2ana_reg->ana_tables.mac_access, + VSC9953_MAC_CMD_MASK | + VSC9953_MAC_ENTRYTYPE_MASK, + VSC9953_MAC_CMD_NEXT); + break; + case MAC_TABLE_AGE: + clrsetbits_le32(&l2ana_reg->ana_tables.mac_access, + VSC9953_MAC_CMD_MASK | + VSC9953_MAC_ENTRYTYPE_MASK, + VSC9953_MAC_CMD_AGE); + break; + default: + printf("Unknown MAC table command\n"); + } + + if (vsc9953_mac_table_poll_idle() < 0) { + debug("MAC table timeout\n"); + return -1; + } + + return 0; +} + +/* show the FDB entries that correspond to a port and a VLAN */ +static void vsc9953_mac_table_show(int port_no, int vid) +{ + int rc[VSC9953_MAX_PORTS]; + enum port_learn_mode mode[VSC9953_MAX_PORTS]; + int i; + u32 val; + u32 vlan; + u32 mach; + u32 macl; + u32 dest_indx; + struct vsc9953_analyzer *l2ana_reg; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + /* disable auto learning */ + if (port_no == ETHSW_CMD_PORT_ALL) { + for (i = 0; i < VSC9953_MAX_PORTS; i++) { + rc[i] = vsc9953_port_learn_mode_get(i, &mode[i]); + if (!rc[i] && mode[i] != PORT_LEARN_NONE) + vsc9953_port_learn_mode_set(i, PORT_LEARN_NONE); + } + } else { + rc[port_no] = vsc9953_port_learn_mode_get(port_no, + &mode[port_no]); + if (!rc[port_no] && mode[port_no] != PORT_LEARN_NONE) + vsc9953_port_learn_mode_set(port_no, PORT_LEARN_NONE); + } + + /* write port and vid to get selected FDB entries */ + val = in_le32(&l2ana_reg->ana.anag_efil); + if (port_no != ETHSW_CMD_PORT_ALL) { + val = bitfield_replace_by_mask(val, VSC9953_AGE_PORT_MASK, + port_no) | VSC9953_AGE_PORT_EN; + } + if (vid != ETHSW_CMD_VLAN_ALL) { + val = bitfield_replace_by_mask(val, VSC9953_AGE_VID_MASK, + vid) | VSC9953_AGE_VID_EN; + } + out_le32(&l2ana_reg->ana.anag_efil, val); + + /* set MAC and VLAN to 0 to look from beginning */ + clrbits_le32(&l2ana_reg->ana_tables.mach_data, + VSC9953_MAC_VID_MASK | VSC9953_MAC_MACH_MASK); + out_le32(&l2ana_reg->ana_tables.macl_data, 0); + + /* get entries */ + printf("%10s %17s %5s %4s\n", "EntryType", "MAC", "PORT", "VID"); + do { + if (vsc9953_mac_table_cmd(MAC_TABLE_GET_NEXT) < 0) { + debug("GET NEXT MAC table command failed\n"); + break; + } + + val = in_le32(&l2ana_reg->ana_tables.mac_access); + + /* get out when an invalid entry is found */ + if (!(val & VSC9953_MAC_CMD_VALID)) + break; + + switch (val & VSC9953_MAC_ENTRYTYPE_MASK) { + case VSC9953_MAC_ENTRYTYPE_NORMAL: + printf("%10s ", "Dynamic"); + break; + case VSC9953_MAC_ENTRYTYPE_LOCKED: + printf("%10s ", "Static"); + break; + case VSC9953_MAC_ENTRYTYPE_IPV4MCAST: + printf("%10s ", "IPv4 Mcast"); + break; + case VSC9953_MAC_ENTRYTYPE_IPV6MCAST: + printf("%10s ", "IPv6 Mcast"); + break; + default: + printf("%10s ", "Unknown"); + } + + dest_indx = bitfield_extract_by_mask(val, + VSC9953_MAC_DESTIDX_MASK); + + val = in_le32(&l2ana_reg->ana_tables.mach_data); + vlan = bitfield_extract_by_mask(val, VSC9953_MAC_VID_MASK); + mach = bitfield_extract_by_mask(val, VSC9953_MAC_MACH_MASK); + macl = in_le32(&l2ana_reg->ana_tables.macl_data); + + printf("%02x:%02x:%02x:%02x:%02x:%02x ", (mach >> 8) & 0xff, + mach & 0xff, (macl >> 24) & 0xff, (macl >> 16) & 0xff, + (macl >> 8) & 0xff, macl & 0xff); + printf("%5d ", dest_indx); + printf("%4d\n", vlan); + } while (1); + + /* set learning mode to previous value */ + if (port_no == ETHSW_CMD_PORT_ALL) { + for (i = 0; i < VSC9953_MAX_PORTS; i++) { + if (!rc[i] && mode[i] != PORT_LEARN_NONE) + vsc9953_port_learn_mode_set(i, mode[i]); + } + } else { + /* If administrative down, skip */ + if (!rc[port_no] && mode[port_no] != PORT_LEARN_NONE) + vsc9953_port_learn_mode_set(port_no, mode[port_no]); + } + + /* reset FDB port and VLAN FDB selection */ + clrbits_le32(&l2ana_reg->ana.anag_efil, VSC9953_AGE_PORT_EN | + VSC9953_AGE_PORT_MASK | VSC9953_AGE_VID_EN | + VSC9953_AGE_VID_MASK); +} + +/* Add a static FDB entry */ +static int vsc9953_mac_table_add(u8 port_no, uchar mac[6], int vid) +{ + u32 val; + struct vsc9953_analyzer *l2ana_reg; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + val = in_le32(&l2ana_reg->ana_tables.mach_data); + val = bitfield_replace_by_mask(val, VSC9953_MACHDATA_VID_MASK, vid) | + (mac[0] << 8) | (mac[1] << 0); + out_le32(&l2ana_reg->ana_tables.mach_data, val); + + out_le32(&l2ana_reg->ana_tables.macl_data, + (mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | + (mac[5] << 0)); + + /* set on which port is the MAC address added */ + val = in_le32(&l2ana_reg->ana_tables.mac_access); + val = bitfield_replace_by_mask(val, VSC9953_MAC_DESTIDX_MASK, port_no); + out_le32(&l2ana_reg->ana_tables.mac_access, val); + + if (vsc9953_mac_table_cmd(MAC_TABLE_LEARN) < 0) + return -1; + + /* check if the MAC address was indeed added */ + val = in_le32(&l2ana_reg->ana_tables.mach_data); + val = bitfield_replace_by_mask(val, VSC9953_MACHDATA_VID_MASK, vid) | + (mac[0] << 8) | (mac[1] << 0); + out_le32(&l2ana_reg->ana_tables.mach_data, val); + + out_le32(&l2ana_reg->ana_tables.macl_data, + (mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | + (mac[5] << 0)); + + if (vsc9953_mac_table_cmd(MAC_TABLE_READ) < 0) + return -1; + + val = in_le32(&l2ana_reg->ana_tables.mac_access); + + if ((port_no != bitfield_extract_by_mask(val, + VSC9953_MAC_DESTIDX_MASK))) { + printf("Failed to add MAC address\n"); + return -1; + } + return 0; +} + +/* Delete a FDB entry */ +static int vsc9953_mac_table_del(uchar mac[6], u16 vid) +{ + u32 val; + struct vsc9953_analyzer *l2ana_reg; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + /* check first if MAC entry is present */ + val = in_le32(&l2ana_reg->ana_tables.mach_data); + val = bitfield_replace_by_mask(val, VSC9953_MACHDATA_VID_MASK, vid) | + (mac[0] << 8) | (mac[1] << 0); + out_le32(&l2ana_reg->ana_tables.mach_data, val); + + out_le32(&l2ana_reg->ana_tables.macl_data, + (mac[2] << 24) | (mac[3] << 16) | (mac[4] << 8) | + (mac[5] << 0)); + + if (vsc9953_mac_table_cmd(MAC_TABLE_LOOKUP) < 0) { + debug("Lookup in the MAC table failed\n"); + return -1; + } + + if (!(in_le32(&l2ana_reg->ana_tables.mac_access) & + VSC9953_MAC_CMD_VALID)) { + printf("The MAC address: %02x:%02x:%02x:%02x:%02x:%02x ", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); + printf("VLAN: %d does not exist.\n", vid); + return -1; + } + + /* FDB entry found, proceed to delete */ + val = in_le32(&l2ana_reg->ana_tables.mach_data); + val = bitfield_replace_by_mask(val, VSC9953_MACHDATA_VID_MASK, vid) | + (mac[0] << 8) | (mac[1] << 0); + out_le32(&l2ana_reg->ana_tables.mach_data, val); + + out_le32(&l2ana_reg->ana_tables.macl_data, (mac[2] << 24) | + (mac[3] << 16) | (mac[4] << 8) | (mac[5] << 0)); + + if (vsc9953_mac_table_cmd(MAC_TABLE_FORGET) < 0) + return -1; + + /* check if the MAC entry is still in FDB */ + val = in_le32(&l2ana_reg->ana_tables.mach_data); + val = bitfield_replace_by_mask(val, VSC9953_MACHDATA_VID_MASK, vid) | + (mac[0] << 8) | (mac[1] << 0); + out_le32(&l2ana_reg->ana_tables.mach_data, val); + + out_le32(&l2ana_reg->ana_tables.macl_data, (mac[2] << 24) | + (mac[3] << 16) | (mac[4] << 8) | (mac[5] << 0)); + + if (vsc9953_mac_table_cmd(MAC_TABLE_LOOKUP) < 0) { + debug("Lookup in the MAC table failed\n"); + return -1; + } + if (in_le32(&l2ana_reg->ana_tables.mac_access) & + VSC9953_MAC_CMD_VALID) { + printf("Failed to delete MAC address\n"); + return -1; + } + + return 0; +} + +/* age the unlocked entries in FDB */ +static void vsc9953_mac_table_age(int port_no, int vid) +{ + int rc[VSC9953_MAX_PORTS]; + enum port_learn_mode mode[VSC9953_MAX_PORTS]; + u32 val; + int i; + struct vsc9953_analyzer *l2ana_reg; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + /* set port and VID for selective aging */ + val = in_le32(&l2ana_reg->ana.anag_efil); + if (port_no != ETHSW_CMD_PORT_ALL) { + /* disable auto learning */ + rc[port_no] = vsc9953_port_learn_mode_get(port_no, + &mode[port_no]); + if (!rc[port_no] && mode[port_no] != PORT_LEARN_NONE) + vsc9953_port_learn_mode_set(port_no, PORT_LEARN_NONE); + + val = bitfield_replace_by_mask(val, VSC9953_AGE_PORT_MASK, + port_no) | VSC9953_AGE_PORT_EN; + } else { + /* disable auto learning on all ports */ + for (i = 0; i < VSC9953_MAX_PORTS; i++) { + rc[i] = vsc9953_port_learn_mode_get(i, &mode[i]); + if (!rc[i] && mode[i] != PORT_LEARN_NONE) + vsc9953_port_learn_mode_set(i, PORT_LEARN_NONE); + } + } + + if (vid != ETHSW_CMD_VLAN_ALL) { + val = bitfield_replace_by_mask(val, VSC9953_AGE_VID_MASK, vid) | + VSC9953_AGE_VID_EN; + } + out_le32(&l2ana_reg->ana.anag_efil, val); + + /* age the dynamic FDB entries */ + vsc9953_mac_table_cmd(MAC_TABLE_AGE); + + /* clear previously set port and VID */ + clrbits_le32(&l2ana_reg->ana.anag_efil, VSC9953_AGE_PORT_EN | + VSC9953_AGE_PORT_MASK | VSC9953_AGE_VID_EN | + VSC9953_AGE_VID_MASK); + + if (port_no != ETHSW_CMD_PORT_ALL) { + if (!rc[port_no] && mode[port_no] != PORT_LEARN_NONE) + vsc9953_port_learn_mode_set(port_no, mode[port_no]); + } else { + for (i = 0; i < VSC9953_MAX_PORTS; i++) { + if (!rc[i] && mode[i] != PORT_LEARN_NONE) + vsc9953_port_learn_mode_set(i, mode[i]); + } + } +} + +/* Delete all the dynamic FDB entries */ +static void vsc9953_mac_table_flush(int port, int vid) +{ + vsc9953_mac_table_age(port, vid); + vsc9953_mac_table_age(port, vid); +} + static int vsc9953_port_status_key_func(struct ethsw_command_def *parsed_cmd) { int i; @@ -949,6 +1332,91 @@ static int vsc9953_learn_set_key_func(struct ethsw_command_def *parsed_cmd) return CMD_RET_SUCCESS; } +static int vsc9953_fdb_show_key_func(struct ethsw_command_def *parsed_cmd) +{ + if (parsed_cmd->port != ETHSW_CMD_PORT_ALL && + !VSC9953_PORT_CHECK(parsed_cmd->port)) { + printf("Invalid port number: %d\n", parsed_cmd->port); + return CMD_RET_FAILURE; + } + + if (parsed_cmd->vid != ETHSW_CMD_VLAN_ALL && + !VSC9953_VLAN_CHECK(parsed_cmd->vid)) { + printf("Invalid VID number: %d\n", parsed_cmd->vid); + return CMD_RET_FAILURE; + } + + vsc9953_mac_table_show(parsed_cmd->port, parsed_cmd->vid); + + return CMD_RET_SUCCESS; +} + +static int vsc9953_fdb_flush_key_func(struct ethsw_command_def *parsed_cmd) +{ + if (parsed_cmd->port != ETHSW_CMD_PORT_ALL && + !VSC9953_PORT_CHECK(parsed_cmd->port)) { + printf("Invalid port number: %d\n", parsed_cmd->port); + return CMD_RET_FAILURE; + } + + if (parsed_cmd->vid != ETHSW_CMD_VLAN_ALL && + !VSC9953_VLAN_CHECK(parsed_cmd->vid)) { + printf("Invalid VID number: %d\n", parsed_cmd->vid); + return CMD_RET_FAILURE; + } + + vsc9953_mac_table_flush(parsed_cmd->port, parsed_cmd->vid); + + return CMD_RET_SUCCESS; +} + +static int vsc9953_fdb_entry_add_key_func(struct ethsw_command_def *parsed_cmd) +{ + int vid; + + /* a port number must be present */ + if (parsed_cmd->port == ETHSW_CMD_PORT_ALL) { + printf("Please specify a port\n"); + return CMD_RET_FAILURE; + } + + if (!VSC9953_PORT_CHECK(parsed_cmd->port)) { + printf("Invalid port number: %d\n", parsed_cmd->port); + return CMD_RET_FAILURE; + } + + /* Use VLAN 1 if VID is not set */ + vid = (parsed_cmd->vid == ETHSW_CMD_VLAN_ALL ? 1 : parsed_cmd->vid); + + if (!VSC9953_VLAN_CHECK(vid)) { + printf("Invalid VID number: %d\n", vid); + return CMD_RET_FAILURE; + } + + if (vsc9953_mac_table_add(parsed_cmd->port, parsed_cmd->ethaddr, vid)) + return CMD_RET_FAILURE; + + return CMD_RET_SUCCESS; +} + +static int vsc9953_fdb_entry_del_key_func(struct ethsw_command_def *parsed_cmd) +{ + int vid; + + /* Use VLAN 1 if VID is not set */ + vid = (parsed_cmd->vid == ETHSW_CMD_VLAN_ALL ? 1 : parsed_cmd->vid); + + if (!VSC9953_VLAN_CHECK(vid)) { + printf("Invalid VID number: %d\n", vid); + return CMD_RET_FAILURE; + } + + if (vsc9953_mac_table_del(parsed_cmd->ethaddr, vid)) + return CMD_RET_FAILURE; + + return CMD_RET_SUCCESS; +} + static struct ethsw_command_func vsc9953_cmd_func = { .ethsw_name = "L2 Switch VSC9953", .port_enable = &vsc9953_port_status_key_func, @@ -958,6 +1426,10 @@ static struct ethsw_command_func vsc9953_cmd_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, + .fdb_show = &vsc9953_fdb_show_key_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, }; #endif /* CONFIG_CMD_ETHSW */ diff --git a/include/ethsw.h b/include/ethsw.h index 6d2f0de..5159031 100644 --- a/include/ethsw.h +++ b/include/ethsw.h @@ -11,6 +11,7 @@ #define ETHSW_MAX_CMD_PARAMS 20 #define ETHSW_CMD_PORT_ALL -1 +#define ETHSW_CMD_VLAN_ALL -1 /* IDs used to track keywords in a command */ enum ethsw_keyword_id { @@ -24,11 +25,19 @@ enum ethsw_keyword_id { ethsw_id_clear, ethsw_id_learning, ethsw_id_auto, + ethsw_id_vlan, + ethsw_id_fdb, + ethsw_id_add, + ethsw_id_del, + ethsw_id_flush, ethsw_id_count, /* keep last */ }; enum ethsw_keyword_opt_id { ethsw_id_port_no = ethsw_id_count + 1, + ethsw_id_vlan_no, + ethsw_id_add_del_no, + ethsw_id_add_del_mac, ethsw_id_count_all, /* keep last */ }; @@ -36,6 +45,8 @@ struct ethsw_command_def { int cmd_to_keywords[ETHSW_MAX_CMD_PARAMS]; int cmd_keywords_nr; int port; + int vid; + uchar ethaddr[6]; int (*cmd_function)(struct ethsw_command_def *parsed_cmd); }; @@ -49,6 +60,10 @@ struct ethsw_command_func { 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 (*fdb_show)(struct ethsw_command_def *parsed_cmd); + 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 ethsw_define_functions(const struct ethsw_command_func *cmd_func); diff --git a/include/vsc9953.h b/include/vsc9953.h index 49215e6..df1c709 100644 --- a/include/vsc9953.h +++ b/include/vsc9953.h @@ -86,6 +86,25 @@ #define VSC9953_VCAP_MV_CFG 0x0000ffff #define VSC9953_VCAP_UPDATE_CTRL 0x01000004 +/* Macros for register vsc9953_ana_ana_tables.mac_access register */ +#define VSC9953_MAC_CMD_IDLE 0x00000000 +#define VSC9953_MAC_CMD_LEARN 0x00000001 +#define VSC9953_MAC_CMD_FORGET 0x00000002 +#define VSC9953_MAC_CMD_AGE 0x00000003 +#define VSC9953_MAC_CMD_NEXT 0x00000004 +#define VSC9953_MAC_CMD_READ 0x00000006 +#define VSC9953_MAC_CMD_WRITE 0x00000007 +#define VSC9953_MAC_CMD_MASK 0x00000007 +#define VSC9953_MAC_CMD_VALID 0x00000800 +#define VSC9953_MAC_ENTRYTYPE_NORMAL 0x00000000 +#define VSC9953_MAC_ENTRYTYPE_LOCKED 0x00000200 +#define VSC9953_MAC_ENTRYTYPE_IPV4MCAST 0x00000400 +#define VSC9953_MAC_ENTRYTYPE_IPV6MCAST 0x00000600 +#define VSC9953_MAC_ENTRYTYPE_MASK 0x00000600 +#define VSC9953_MAC_DESTIDX_MASK 0x000001f8 +#define VSC9953_MAC_VID_MASK 0x1fff0000 +#define VSC9953_MAC_MACH_MASK 0x0000ffff + /* Macros for vsc9953_ana_port.vlan_cfg register */ #define VSC9953_VLAN_CFG_AWARE_ENA 0x00100000 #define VSC9953_VLAN_CFG_POP_CNT_MASK 0x000c0000 @@ -124,6 +143,15 @@ #define VSC9953_TAG_CFG_ALL_BUT_ZERO 0x00000100 #define VSC9953_TAG_CFG_ALL 0x00000180 +/* Macros for vsc9953_ana_ana.anag_efil register */ +#define VSC9953_AGE_PORT_EN 0x00080000 +#define VSC9953_AGE_PORT_MASK 0x0007c000 +#define VSC9953_AGE_VID_EN 0x00002000 +#define VSC9953_AGE_VID_MASK 0x00001fff + +/* Macros for vsc9953_ana_ana_tables.mach_data register */ +#define VSC9953_MACHDATA_VID_MASK 0x1fff0000 + #define VSC9953_MAX_PORTS 10 #define VSC9953_PORT_CHECK(port) \ (((port) < 0 || (port) >= VSC9953_MAX_PORTS) ? 0 : 1) -- cgit v0.10.2 From a2477924cd302cfae730ebefb431814eb99ad861 Mon Sep 17 00:00:00 2001 From: Codrin Ciubotariu Date: Fri, 24 Jul 2015 16:55:33 +0300 Subject: drivers/net/vsc9953: Add VLAN commands for VSC9953 The new added commands can be used to configure VLANs for a port on both ingress and egress. The new commands are: ethsw [port ] pvid { [help] | show | } - set/show PVID (ingress and egress VLAN tagging) for a port; ethsw [port ] vlan { [help] | show | add | del } - add a VLAN to a port (VLAN members); ethsw [port ] untagged { [help] | show | all | none | pvid } - set egress tagging mod for a port" ethsw [port ] 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 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 9ba680a..1fa93ef 100644 --- a/common/cmd_ethsw.c +++ b/common/cmd_ethsw.c @@ -47,6 +47,51 @@ static int ethsw_fdb_help_key_func(struct ethsw_command_def *parsed_cmd) return CMD_RET_SUCCESS; } +#define ETHSW_PVID_HELP "ethsw [port ] " \ +"pvid { [help] | show | } " \ +"- 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 ] vlan " \ +"{ [help] | show | add | del } " \ +"- 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 ] 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 ] 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; @@ -196,6 +241,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 = ðsw_pvid_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_pvid, + ethsw_id_help, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_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 = ðsw_vlan_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_vlan, + ethsw_id_help, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_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 = ðsw_port_untag_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_untagged, + ethsw_id_help, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_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 = ðsw_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 = ðsw_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, }, }; @@ -234,6 +454,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); @@ -289,6 +512,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, }, }; @@ -403,6 +647,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, @@ -624,4 +890,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 071906c..c5c0e5c 100644 --- a/drivers/net/vsc9953.c +++ b/drivers/net/vsc9953.c @@ -196,6 +196,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) { @@ -233,6 +327,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) { @@ -359,6 +477,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) { @@ -1166,6 +1353,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; @@ -1417,6 +1649,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, @@ -1430,6 +1900,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 -- cgit v0.10.2 From 21d214fcd0a12c75ebb9c675f0f07b8e69f6cb44 Mon Sep 17 00:00:00 2001 From: Codrin Ciubotariu Date: Fri, 24 Jul 2015 16:55:34 +0300 Subject: drivers/net/vsc9953: Add command for shared/private VLAN learning The command: ethsw vlan fdb { [help] | show | shared | private } - make VLAN learning shared or private" configures the FDB to share the FDB entries learned on multiple VLANs or to keep them separated. By default, the FBD uses private VLAN learning. This command has also been added to the ethsw generic parser from common/cmd_ethsw.c 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 1fa93ef..8cc2a19 100644 --- a/common/cmd_ethsw.c +++ b/common/cmd_ethsw.c @@ -92,6 +92,17 @@ static int ethsw_egr_tag_help_key_func(struct ethsw_command_def *parsed_cmd) return CMD_RET_SUCCESS; } +#define ETHSW_VLAN_FDB_HELP "ethsw vlan fdb " \ +"{ [help] | show | shared | private } " \ +"- make VLAN learning shared or private" + +static int ethsw_vlan_learn_help_key_func(struct ethsw_command_def *parsed_cmd) +{ + printf(ETHSW_VLAN_FDB_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; @@ -416,6 +427,53 @@ static struct keywords_to_function { .cmd_func_offset = offsetof(struct ethsw_command_func, port_egr_vlan_set), .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_vlan, + ethsw_id_fdb, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_vlan_learn_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_vlan, + ethsw_id_fdb, + ethsw_id_help, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_vlan_learn_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_vlan, + ethsw_id_fdb, + ethsw_id_show, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + vlan_learn_show), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_vlan, + ethsw_id_fdb, + ethsw_id_shared, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + vlan_learn_set), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_vlan, + ethsw_id_fdb, + ethsw_id_private, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + vlan_learn_set), + .keyword_function = NULL, }, }; @@ -533,6 +591,12 @@ struct keyword_def { }, { .keyword_name = "classified", .match = &keyword_match_gen, + }, { + .keyword_name = "shared", + .match = &keyword_match_gen, + }, { + .keyword_name = "private", + .match = &keyword_match_gen, }, }; @@ -894,4 +958,5 @@ U_BOOT_CMD(ethsw, ETHSW_MAX_CMD_PARAMS, 0, do_ethsw, ETHSW_VLAN_HELP"\n" ETHSW_PORT_UNTAG_HELP"\n" ETHSW_EGR_VLAN_TAG_HELP"\n" + ETHSW_VLAN_FDB_HELP"\n" ); diff --git a/drivers/net/vsc9953.c b/drivers/net/vsc9953.c index c5c0e5c..f8f5233 100644 --- a/drivers/net/vsc9953.c +++ b/drivers/net/vsc9953.c @@ -1398,6 +1398,55 @@ static void vsc9953_port_vlan_egress_tag_get(int port_no, *mode = EGR_TAG_CLASS; } +/* VSC9953 VLAN learning modes */ +enum vlan_learning_mode { + SHARED_VLAN_LEARNING, + PRIVATE_VLAN_LEARNING, +}; + +/* Set VLAN learning mode for VSC9953 */ +static void vsc9953_vlan_learning_set(enum vlan_learning_mode lrn_mode) +{ + struct vsc9953_analyzer *l2ana_reg; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + switch (lrn_mode) { + case SHARED_VLAN_LEARNING: + setbits_le32(&l2ana_reg->ana.agen_ctrl, VSC9953_FID_MASK_ALL); + break; + case PRIVATE_VLAN_LEARNING: + clrbits_le32(&l2ana_reg->ana.agen_ctrl, VSC9953_FID_MASK_ALL); + break; + default: + printf("Unknown VLAN learn mode\n"); + } +} + +/* Get VLAN learning mode for VSC9953 */ +static int vsc9953_vlan_learning_get(enum vlan_learning_mode *lrn_mode) +{ + u32 val; + struct vsc9953_analyzer *l2ana_reg; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + val = in_le32(&l2ana_reg->ana.agen_ctrl); + + if (!(val & VSC9953_FID_MASK_ALL)) { + *lrn_mode = PRIVATE_VLAN_LEARNING; + } else if ((val & VSC9953_FID_MASK_ALL) == VSC9953_FID_MASK_ALL) { + *lrn_mode = SHARED_VLAN_LEARNING; + } else { + printf("Unknown VLAN learning mode\n"); + return -EINVAL; + } + + return 0; +} + static int vsc9953_port_status_key_func(struct ethsw_command_def *parsed_cmd) { int i; @@ -1887,6 +1936,50 @@ static int vsc9953_egr_vlan_tag_set_key_func( return CMD_RET_SUCCESS; } +static int vsc9953_vlan_learn_show_key_func( + struct ethsw_command_def *parsed_cmd) +{ + int rc; + enum vlan_learning_mode mode; + + rc = vsc9953_vlan_learning_get(&mode); + if (rc) + return CMD_RET_FAILURE; + + switch (mode) { + case SHARED_VLAN_LEARNING: + printf("VLAN learning mode: shared\n"); + break; + case PRIVATE_VLAN_LEARNING: + printf("VLAN learning mode: private\n"); + break; + default: + printf("Unknown VLAN learning mode\n"); + rc = CMD_RET_FAILURE; + } + + return CMD_RET_SUCCESS; +} + +static int vsc9953_vlan_learn_set_key_func(struct ethsw_command_def *parsed_cmd) +{ + enum vlan_learning_mode mode; + + /* keywords for shared/private are the last in the array */ + if (parsed_cmd->cmd_to_keywords[parsed_cmd->cmd_keywords_nr - 1] == + ethsw_id_shared) + mode = SHARED_VLAN_LEARNING; + else if (parsed_cmd->cmd_to_keywords[parsed_cmd->cmd_keywords_nr - 1] == + ethsw_id_private) + mode = PRIVATE_VLAN_LEARNING; + else + return CMD_RET_USAGE; + + vsc9953_vlan_learning_set(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, @@ -1908,6 +2001,8 @@ static struct ethsw_command_func vsc9953_cmd_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, + .vlan_learn_show = &vsc9953_vlan_learn_show_key_func, + .vlan_learn_set = &vsc9953_vlan_learn_set_key_func, }; #endif /* CONFIG_CMD_ETHSW */ diff --git a/include/ethsw.h b/include/ethsw.h index cc9708e..18d2b26 100644 --- a/include/ethsw.h +++ b/include/ethsw.h @@ -37,6 +37,8 @@ enum ethsw_keyword_id { ethsw_id_egress, ethsw_id_tag, ethsw_id_classified, + ethsw_id_shared, + ethsw_id_private, ethsw_id_count, /* keep last */ }; @@ -80,6 +82,8 @@ struct ethsw_command_func { 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 (*vlan_learn_show)(struct ethsw_command_def *parsed_cmd); + int (*vlan_learn_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 12b7ace..24a22a3 100644 --- a/include/vsc9953.h +++ b/include/vsc9953.h @@ -135,6 +135,9 @@ /* Macros for vsc9953_qsys_sys.switch_port_mode register */ #define VSC9953_PORT_ENA 0x00002000 +/* Macros for vsc9953_ana_ana.agen_ctrl register */ +#define VSC9953_FID_MASK_ALL 0x00fff000 + /* Macros for vsc9953_ana_ana.adv_learn register */ #define VSC9953_VLAN_CHK 0x00000400 -- cgit v0.10.2 From 5ed1bacd3402a3153c8306766ad099b2261ebe36 Mon Sep 17 00:00:00 2001 From: Codrin Ciubotariu Date: Fri, 24 Jul 2015 16:55:35 +0300 Subject: drivers/net/vsc9953: Add commands for VLAN ingress filtering The command: ethsw [port ] ingress filtering { [help] | show | enable | disable } - enable/disable VLAN ingress filtering on port can be used to enable/disable/show VLAN ingress filtering on a port. This command has also been added to the ethsw generic parser from common/cmd_ethsw.c 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 8cc2a19..8e452e9 100644 --- a/common/cmd_ethsw.c +++ b/common/cmd_ethsw.c @@ -103,6 +103,17 @@ static int ethsw_vlan_learn_help_key_func(struct ethsw_command_def *parsed_cmd) return CMD_RET_SUCCESS; } +#define ETHSW_PORT_INGR_FLTR_HELP "ethsw [port ] ingress filtering" \ +" { [help] | show | enable | disable } " \ +"- enable/disable VLAN ingress filtering on port" + +static int ethsw_ingr_fltr_help_key_func(struct ethsw_command_def *parsed_cmd) +{ + printf(ETHSW_PORT_INGR_FLTR_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; @@ -474,6 +485,53 @@ static struct keywords_to_function { .cmd_func_offset = offsetof(struct ethsw_command_func, vlan_learn_set), .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_ingress, + ethsw_id_filtering, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_ingr_fltr_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_ingress, + ethsw_id_filtering, + ethsw_id_help, + ethsw_id_key_end, + }, + .cmd_func_offset = -1, + .keyword_function = ðsw_ingr_fltr_help_key_func, + }, { + .cmd_keyword = { + ethsw_id_ingress, + ethsw_id_filtering, + ethsw_id_show, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_ingr_filt_show), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_ingress, + ethsw_id_filtering, + ethsw_id_enable, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_ingr_filt_set), + .keyword_function = NULL, + }, { + .cmd_keyword = { + ethsw_id_ingress, + ethsw_id_filtering, + ethsw_id_disable, + ethsw_id_key_end, + }, + .cmd_func_offset = offsetof(struct ethsw_command_func, + port_ingr_filt_set), + .keyword_function = NULL, }, }; @@ -597,6 +655,12 @@ struct keyword_def { }, { .keyword_name = "private", .match = &keyword_match_gen, + }, { + .keyword_name = "ingress", + .match = &keyword_match_gen, + }, { + .keyword_name = "filtering", + .match = &keyword_match_gen, }, }; @@ -959,4 +1023,5 @@ U_BOOT_CMD(ethsw, ETHSW_MAX_CMD_PARAMS, 0, do_ethsw, ETHSW_PORT_UNTAG_HELP"\n" ETHSW_EGR_VLAN_TAG_HELP"\n" ETHSW_VLAN_FDB_HELP"\n" + ETHSW_PORT_INGR_FLTR_HELP"\n" ); diff --git a/drivers/net/vsc9953.c b/drivers/net/vsc9953.c index f8f5233..8be87a1 100644 --- a/drivers/net/vsc9953.c +++ b/drivers/net/vsc9953.c @@ -1447,6 +1447,33 @@ static int vsc9953_vlan_learning_get(enum vlan_learning_mode *lrn_mode) return 0; } +/* Enable/disable VLAN ingress filtering on a VSC9953 port */ +static void vsc9953_port_ingress_filtering_set(int port_no, int enabled) +{ + struct vsc9953_analyzer *l2ana_reg; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + if (enabled) + setbits_le32(&l2ana_reg->ana.vlan_mask, 1 << port_no); + else + clrbits_le32(&l2ana_reg->ana.vlan_mask, 1 << port_no); +} + +/* Return VLAN ingress filtering on a VSC9953 port */ +static int vsc9953_port_ingress_filtering_get(int port_no) +{ + u32 val; + struct vsc9953_analyzer *l2ana_reg; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + val = in_le32(&l2ana_reg->ana.vlan_mask); + return !!(val & (1 << port_no)); +} + static int vsc9953_port_status_key_func(struct ethsw_command_def *parsed_cmd) { int i; @@ -1980,6 +2007,63 @@ static int vsc9953_vlan_learn_set_key_func(struct ethsw_command_def *parsed_cmd) return CMD_RET_SUCCESS; } +static int vsc9953_ingr_fltr_show_key_func(struct ethsw_command_def *parsed_cmd) +{ + int i; + int enabled; + + printf("%7s\t%18s\n", "Port", "Ingress filtering"); + 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; + } + enabled = vsc9953_port_ingress_filtering_get(parsed_cmd->port); + printf("%7d\t%18s\n", parsed_cmd->port, enabled ? "enable" : + "disable"); + } else { + for (i = 0; i < VSC9953_MAX_PORTS; i++) { + enabled = vsc9953_port_ingress_filtering_get(i); + printf("%7d\t%18s\n", parsed_cmd->port, enabled ? + "enable" : + "disable"); + } + } + + return CMD_RET_SUCCESS; +} + +static int vsc9953_ingr_fltr_set_key_func(struct ethsw_command_def *parsed_cmd) +{ + int i; + int enable; + + /* keywords for enabling/disabling ingress filtering + * are the last in the array + */ + if (parsed_cmd->cmd_to_keywords[parsed_cmd->cmd_keywords_nr - 1] == + ethsw_id_enable) + enable = 1; + else if (parsed_cmd->cmd_to_keywords[parsed_cmd->cmd_keywords_nr - 1] == + ethsw_id_disable) + enable = 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_port_ingress_filtering_set(parsed_cmd->port, enable); + } else { + for (i = 0; i < VSC9953_MAX_PORTS; i++) + vsc9953_port_ingress_filtering_set(i, enable); + } + + return CMD_RET_SUCCESS; +} + static struct ethsw_command_func vsc9953_cmd_func = { .ethsw_name = "L2 Switch VSC9953", .port_enable = &vsc9953_port_status_key_func, @@ -2003,6 +2087,8 @@ static struct ethsw_command_func vsc9953_cmd_func = { .port_egr_vlan_set = &vsc9953_egr_vlan_tag_set_key_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 }; #endif /* CONFIG_CMD_ETHSW */ diff --git a/include/ethsw.h b/include/ethsw.h index 18d2b26..2d3c12a 100644 --- a/include/ethsw.h +++ b/include/ethsw.h @@ -39,6 +39,8 @@ enum ethsw_keyword_id { ethsw_id_classified, ethsw_id_shared, ethsw_id_private, + ethsw_id_ingress, + ethsw_id_filtering, ethsw_id_count, /* keep last */ }; @@ -84,6 +86,8 @@ struct ethsw_command_func { int (*port_egr_vlan_set)(struct ethsw_command_def *parsed_cmd); int (*vlan_learn_show)(struct ethsw_command_def *parsed_cmd); 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 ethsw_define_functions(const struct ethsw_command_func *cmd_func); -- cgit v0.10.2 From a857d5f835c53f7b66de61b19120ea1188e94999 Mon Sep 17 00:00:00 2001 From: Codrin Ciubotariu Date: Fri, 24 Jul 2015 16:55:36 +0300 Subject: drivers/net/vsc9953: Add GPL-2.0+ SPDX-License-Identifier Signed-off-by: Codrin Ciubotariu Acked-by: Joe Hershberger Reviewed-by: York Sun diff --git a/drivers/net/vsc9953.c b/drivers/net/vsc9953.c index 8be87a1..7595db1 100644 --- a/drivers/net/vsc9953.c +++ b/drivers/net/vsc9953.c @@ -1,5 +1,5 @@ /* - * Copyright 2014 Freescale Semiconductor, Inc. + * Copyright 2014 - 2015 Freescale Semiconductor, Inc. * * SPDX-License-Identifier: GPL-2.0+ * diff --git a/include/vsc9953.h b/include/vsc9953.h index 24a22a3..cd5cfc7 100644 --- a/include/vsc9953.h +++ b/include/vsc9953.h @@ -1,14 +1,9 @@ /* - * vsc9953.h + * Copyright 2013, 2015 Freescale Semiconductor, Inc. * - * Driver for the Vitesse VSC9953 L2 Switch - * - * This software may be used and distributed according to the - * terms of the GNU Public License, Version 2, incorporated - * herein by reference. - * - * Copyright 2013 Freescale Semiconductor, Inc. + * SPDX-License-Identifier: GPL-2.0+ * + * Driver for the Vitesse VSC9953 L2 Switch */ #ifndef _VSC9953_H_ -- cgit v0.10.2