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 <johnson.le...@freescale.com> Signed-off-by: Codrin Ciubotariu <codrin.ciubota...@freescale.com> Change-Id: I2cd5ad30371ca8f64aec51afe5bd6b3c0a66569d --- drivers/net/vsc9953.c | 266 +++++++++++++++++++++++++++++++++++++++++++++++++- include/vsc9953.h | 61 +++++++++++- 2 files changed, 325 insertions(+), 2 deletions(-) diff --git a/drivers/net/vsc9953.c b/drivers/net/vsc9953.c index 720ae47..9dec683 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+ * @@ -176,6 +176,268 @@ 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) & + CONFIG_VSC9953_VLAN_CMD_MASK) != + CONFIG_VSC9953_VLAN_CMD_IDLE) && --timeout) + udelay(1); + + return !!timeout; +} + +/* vlan table set/clear all membership of vid */ +static void vsc9953_vlan_table_membership_all_set(int vid, int set) +{ + struct vsc9953_analyzer *l2ana_reg; + + l2ana_reg = (struct vsc9953_analyzer *)(VSC9953_OFFSET + + VSC9953_ANA_OFFSET); + + if (!vsc9953_vlan_table_poll_idle()) { + debug("VLAN table timeout\n"); + return; + } + + /* read current vlan configuration */ + clrsetbits_le32(&l2ana_reg->ana_tables.vlan_tidx, + CONFIG_VSC9953_ANA_TBL_VID_MASK, + field_set(vid, CONFIG_VSC9953_ANA_TBL_VID_MASK)); + + clrsetbits_le32(&l2ana_reg->ana_tables.vlan_access, + CONFIG_VSC9953_VLAN_CMD_MASK, + field_set(CONFIG_VSC9953_VLAN_CMD_READ, + CONFIG_VSC9953_VLAN_CMD_MASK)); + + if (!vsc9953_vlan_table_poll_idle()) { + debug("VLAN table timeout\n"); + return; + } + + clrsetbits_le32(&l2ana_reg->ana_tables.vlan_tidx, + CONFIG_VSC9953_ANA_TBL_VID_MASK, + field_set(vid, CONFIG_VSC9953_ANA_TBL_VID_MASK)); + + if (!set) + clrsetbits_le32(&l2ana_reg->ana_tables.vlan_access, + CONFIG_VSC9953_VLAN_PORT_MASK | + CONFIG_VSC9953_VLAN_CMD_MASK, + field_set(CONFIG_VSC9953_VLAN_CMD_WRITE, + CONFIG_VSC9953_VLAN_CMD_MASK)); + else + clrsetbits_le32(&l2ana_reg->ana_tables.vlan_access, + CONFIG_VSC9953_VLAN_PORT_MASK | + CONFIG_VSC9953_VLAN_CMD_MASK, + field_set(CONFIG_VSC9953_VLAN_CMD_WRITE, + CONFIG_VSC9953_VLAN_CMD_MASK) | + CONFIG_VSC9953_VLAN_PORT_MASK); +} + +/* Set PVID for a VSC9953 port */ +static void vsc9953_port_vlan_pvid_set(int port_no, int pvid) +{ + 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 */ + clrsetbits_le32(&l2ana_reg->port[port_no].vlan_cfg, + CONFIG_VSC9953_VLAN_CFG_VID_MASK, + field_set(pvid, CONFIG_VSC9953_VLAN_CFG_VID_MASK)); + + /* Set PVID on egress */ + clrsetbits_le32(&l2rew_reg->port[port_no].port_vlan_cfg, + CONFIG_VSC9953_PORT_VLAN_CFG_VID_MASK, + field_set(pvid, CONFIG_VSC9953_PORT_VLAN_CFG_VID_MASK)); +} + +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, + CONFIG_VSC9953_VLAN_CFG_AWARE_ENA); + else + clrbits_le32(&l2ana_reg->port[port_no].vlan_cfg, + CONFIG_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) +{ + 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); + + clrsetbits_le32(&l2ana_reg->port[port_no].vlan_cfg, + CONFIG_VSC9953_VLAN_CFG_POP_CNT_MASK, + field_set(popcnt, + CONFIG_VSC9953_VLAN_CFG_POP_CNT_MASK)); +} + +/* 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, + CONFIG_VSC9953_VLAN_CHK); + else + clrbits_le32(&l2ana_reg->ana.adv_learn, + CONFIG_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, + CONFIG_VSC9953_TAG_CFG_MASK, + CONFIG_VSC9953_TAG_CFG_NONE); + break; + case EGRESS_UNTAG_PVID_AND_ZERO: + clrsetbits_le32(&l2rew_reg->port[port_no].port_tag_cfg, + CONFIG_VSC9953_TAG_CFG_MASK, + CONFIG_VSC9953_TAG_CFG_ALL_PVID_ZERO); + break; + case EGRESS_UNTAG_ZERO: + clrsetbits_le32(&l2rew_reg->port[port_no].port_tag_cfg, + CONFIG_VSC9953_TAG_CFG_MASK, + CONFIG_VSC9953_TAG_CFG_ALL_ZERO); + break; + case EGRESS_UNTAG_NONE: + clrsetbits_le32(&l2rew_reg->port[port_no].port_tag_cfg, + CONFIG_VSC9953_TAG_CFG_MASK, + CONFIG_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, hdx_cfg = 0, phy_addr = 0; @@ -306,6 +568,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 2b88c5c..bf81623 100644 --- a/include/vsc9953.h +++ b/include/vsc9953.h @@ -7,7 +7,7 @@ * terms of the GNU Public License, Version 2, incorporated * herein by reference. * - * Copyright 2013 Freescale Semiconductor, Inc. + * Copyright 2013, 2015 Freescale Semiconductor, Inc. * */ @@ -19,9 +19,13 @@ #include <asm/types.h> #include <malloc.h> +#define field_set(val, mask) ((val) * ((mask) & ~((mask) << 1))) +#define field_get(val, mask) ((val) / ((mask) & ~((mask) << 1))) + #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 @@ -84,9 +88,38 @@ #define CONFIG_VSC9953_VCAP_MV_CFG 0x0000ffff #define CONFIG_VSC9953_VCAP_UPDATE_CTRL 0x01000004 +/* Macros for vsc9953_ana_port.vlan_cfg register */ +#define CONFIG_VSC9953_VLAN_CFG_AWARE_ENA 0x00100000 +#define CONFIG_VSC9953_VLAN_CFG_POP_CNT_MASK 0x000c0000 +#define CONFIG_VSC9953_VLAN_CFG_VID_MASK 0x00000fff + +/* Macros for vsc9953_rew_port.port_vlan_cfg register */ +#define CONFIG_VSC9953_PORT_VLAN_CFG_VID_MASK 0x00000fff + +/* Macros for vsc9953_ana_ana_tables.vlan_tidx register */ +#define CONFIG_VSC9953_ANA_TBL_VID_MASK 0x00000fff + +/* Macros for vsc9953_ana_ana_tables.vlan_access register */ +#define CONFIG_VSC9953_VLAN_PORT_MASK 0x00001ffc +#define CONFIG_VSC9953_VLAN_CMD_MASK 0x00000003 +#define CONFIG_VSC9953_VLAN_CMD_IDLE 0x00000000 +#define CONFIG_VSC9953_VLAN_CMD_READ 0x00000001 +#define CONFIG_VSC9953_VLAN_CMD_WRITE 0x00000002 +#define CONFIG_VSC9953_VLAN_CMD_INIT 0x00000003 + /* Macros for vsc9953_qsys_sys.switch_port_mode register */ #define CONFIG_VSC9953_PORT_ENA 0x00002000 +/* Macros for vsc9953_ana_ana.adv_learn register */ +#define CONFIG_VSC9953_VLAN_CHK 0x00000400 + +/* Macros for vsc9953_rew_port.port_tag_cfg register */ +#define CONFIG_VSC9953_TAG_CFG_MASK 0x00000180 +#define CONFIG_VSC9953_TAG_CFG_NONE 0x00000000 +#define CONFIG_VSC9953_TAG_CFG_ALL_PVID_ZERO 0x00000080 +#define CONFIG_VSC9953_TAG_CFG_ALL_ZERO 0x00000100 +#define CONFIG_VSC9953_TAG_CFG_ALL 0x00000180 + #define VSC9953_MAX_PORTS 10 #define VSC9953_PORT_CHECK(port) \ (((port) < 0 || (port) >= VSC9953_MAX_PORTS) ? 0 : 1) @@ -95,6 +128,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" @@ -342,6 +378,29 @@ struct vsc9953_system_reg { /* END VSC9953 SYS structure for T1040 U-boot*/ +/* VSC9953 REW structure for T1040 U-boot*/ + +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 for T1040 U-boot*/ /* VSC9953 DEVCPU_GCB structure for T1040 U-boot*/ -- 1.9.3 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot