drivers/net/vsc9953: Add default configuration for VSC9953 L2 Switch
authorCodrin Ciubotariu <codrin.ciubotariu@freescale.com>
Fri, 24 Jul 2015 13:55:26 +0000 (16:55 +0300)
committerYork Sun <yorksun@freescale.com>
Mon, 21 Sep 2015 15:29:46 +0000 (08:29 -0700)
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.leung@freescale.com>
Signed-off-by: Codrin Ciubotariu <codrin.ciubotariu@freescale.com>
Acked-by: Joe Hershberger <joe.hershberger@ni.com>
Reviewed-by: York Sun <yorksun@freescale.com>
drivers/net/vsc9953.c
include/vsc9953.h

index 2e8eec41f8e59ce94a6d9a2d4572b77b9aa708ad..59e0fabbd97954e250acbf0aefd20d09fa048395 100644 (file)
@@ -10,6 +10,7 @@
 #include <asm/fsl_serdes.h>
 #include <fm_eth.h>
 #include <fsl_memac.h>
+#include <bitfield.h>
 #include <errno.h>
 #include <malloc.h>
 #include <vsc9953.h>
@@ -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;
 }
index 8ff02c15aa7fd70dc97ce6b1243527255e9ae121..bb9e22df2f189b03a2ca215eb0accfef194844ab 100644 (file)
@@ -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
 #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)
                (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 */