From: "hongbo.wang" <hongbo.w...@nxp.com> This featue can be test using network test tools TX-tool -----> swp0 -----> swp1 -----> RX-tool
TX-tool simulates Customer that will send and receive packets with single VLAN tag(CTAG), RX-tool simulates Service-Provider that will send and receive packets with double VLAN tag(STAG and CTAG). This refers to "4.3.3 Provider Bridges and Q-in-Q Operation" in VSC99599_1_00_TS.pdf. The related test commands: 1. ip link add dev br0 type bridge ip link set dev swp1 master br0 ip link set br0 type bridge vlan_protocol 802.1ad ip link set dev swp0 master br0 2. ip link set dev br0 type bridge vlan_filtering 1 bridge vlan add dev swp0 vid 100 pvid bridge vlan add dev swp1 vid 100 Result: Customer(tpid:8100 vid:111) -> swp0 -> swp1 -> ISP(STAG \ tpid:88A8 vid:100, CTAG tpid:8100 vid:111) 3. bridge vlan del dev swp0 vid 1 pvid bridge vlan add dev swp0 vid 100 pvid untagged Result: ISP(tpid:88A8 vid:100 tpid:8100 vid:222) -> swp1 -> swp0 ->\ Customer(tpid:8100 vid:222) Signed-off-by: hongbo.wang <hongbo.w...@nxp.com> --- drivers/net/dsa/ocelot/felix.c | 12 +++++++ drivers/net/ethernet/mscc/ocelot.c | 53 +++++++++++++++++++++++++----- include/soc/mscc/ocelot.h | 2 ++ 3 files changed, 59 insertions(+), 8 deletions(-) diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index c69d9592a2b7..72a27b61080e 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -131,10 +131,16 @@ static void felix_vlan_add(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan) { struct ocelot *ocelot = ds->priv; + struct ocelot_port *ocelot_port = ocelot->ports[port]; u16 flags = vlan->flags; u16 vid; int err; + if (vlan->proto == ETH_P_8021AD) { + ocelot->enable_qinq = true; + ocelot_port->qinq_mode = true; + } + if (dsa_is_cpu_port(ds, port)) flags &= ~BRIDGE_VLAN_INFO_UNTAGGED; @@ -154,9 +160,15 @@ static int felix_vlan_del(struct dsa_switch *ds, int port, const struct switchdev_obj_port_vlan *vlan) { struct ocelot *ocelot = ds->priv; + struct ocelot_port *ocelot_port = ocelot->ports[port]; u16 vid; int err; + if (vlan->proto == ETH_P_8021AD) { + ocelot->enable_qinq = false; + ocelot_port->qinq_mode = false; + } + for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) { err = ocelot_vlan_del(ocelot, port, vid); if (err) { diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index f2d94b026d88..b5fec6855afd 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -143,6 +143,8 @@ static int ocelot_port_set_native_vlan(struct ocelot *ocelot, int port, u16 vid) { struct ocelot_port *ocelot_port = ocelot->ports[port]; + u32 port_tpid = 0; + u32 tag_tpid = 0; u32 val = 0; if (ocelot_port->vid != vid) { @@ -156,8 +158,14 @@ static int ocelot_port_set_native_vlan(struct ocelot *ocelot, int port, ocelot_port->vid = vid; } - ocelot_rmw_gix(ocelot, REW_PORT_VLAN_CFG_PORT_VID(vid), - REW_PORT_VLAN_CFG_PORT_VID_M, + if (ocelot_port->qinq_mode) + port_tpid = REW_PORT_VLAN_CFG_PORT_TPID(ETH_P_8021AD); + else + port_tpid = REW_PORT_VLAN_CFG_PORT_TPID(ETH_P_8021Q); + + ocelot_rmw_gix(ocelot, REW_PORT_VLAN_CFG_PORT_VID(vid) | port_tpid, + REW_PORT_VLAN_CFG_PORT_VID_M | + REW_PORT_VLAN_CFG_PORT_TPID_M, REW_PORT_VLAN_CFG, port); if (ocelot_port->vlan_aware && !ocelot_port->vid) @@ -180,12 +188,28 @@ static int ocelot_port_set_native_vlan(struct ocelot *ocelot, int port, else /* Tag all frames */ val = REW_TAG_CFG_TAG_CFG(3); + + if (ocelot_port->qinq_mode) + tag_tpid = REW_TAG_CFG_TAG_TPID_CFG(1); + else + tag_tpid = REW_TAG_CFG_TAG_TPID_CFG(0); } else { - /* Port tagging disabled. */ - val = REW_TAG_CFG_TAG_CFG(0); + if (ocelot_port->qinq_mode) { + if (ocelot_port->vid) + val = REW_TAG_CFG_TAG_CFG(1); + else + val = REW_TAG_CFG_TAG_CFG(3); + + tag_tpid = REW_TAG_CFG_TAG_TPID_CFG(1); + } else { + /* Port tagging disabled. */ + val = REW_TAG_CFG_TAG_CFG(0); + tag_tpid = REW_TAG_CFG_TAG_TPID_CFG(0); + } } - ocelot_rmw_gix(ocelot, val, - REW_TAG_CFG_TAG_CFG_M, + + ocelot_rmw_gix(ocelot, val | tag_tpid, + REW_TAG_CFG_TAG_CFG_M | REW_TAG_CFG_TAG_TPID_CFG_M, REW_TAG_CFG, port); return 0; @@ -204,6 +228,15 @@ void ocelot_port_vlan_filtering(struct ocelot *ocelot, int port, ANA_PORT_VLAN_CFG_VLAN_POP_CNT(1); else val = 0; + + /* if switch is enabled for QinQ, the port for LAN should set + * VLAN_CFG.VLAN_POP_CNT=0 && VLAN_CFG.VLAN_AWARE_ENA=0. + * the port for MAN should set VLAN_CFG.VLAN_POP_CNT=1 && + * VLAN_CFG.VLAN_AWARE_ENA=1. referring to 4.3.3 in VSC9959_1_00_TS.pdf + */ + if (ocelot->enable_qinq && !ocelot_port->qinq_mode) + val = 0; + ocelot_rmw_gix(ocelot, val, ANA_PORT_VLAN_CFG_VLAN_AWARE_ENA | ANA_PORT_VLAN_CFG_VLAN_POP_CNT_M, @@ -217,10 +250,14 @@ EXPORT_SYMBOL(ocelot_port_vlan_filtering); static void ocelot_port_set_pvid(struct ocelot *ocelot, int port, u16 pvid) { struct ocelot_port *ocelot_port = ocelot->ports[port]; + u32 tag_type = 0; + + if (ocelot_port->qinq_mode) + tag_type = ANA_PORT_VLAN_CFG_VLAN_TAG_TYPE; ocelot_rmw_gix(ocelot, - ANA_PORT_VLAN_CFG_VLAN_VID(pvid), - ANA_PORT_VLAN_CFG_VLAN_VID_M, + ANA_PORT_VLAN_CFG_VLAN_VID(pvid) | tag_type, + ANA_PORT_VLAN_CFG_VLAN_VID_M | tag_type, ANA_PORT_VLAN_CFG, port); ocelot_port->pvid = pvid; diff --git a/include/soc/mscc/ocelot.h b/include/soc/mscc/ocelot.h index da369b12005f..59020bb8fe68 100644 --- a/include/soc/mscc/ocelot.h +++ b/include/soc/mscc/ocelot.h @@ -556,6 +556,7 @@ struct ocelot_port { struct regmap *target; bool vlan_aware; + bool qinq_mode; /* Ingress default VLAN (pvid) */ u16 pvid; @@ -632,6 +633,7 @@ struct ocelot { /* Protects the PTP clock */ spinlock_t ptp_clock_lock; struct ptp_pin_desc ptp_pins[OCELOT_PTP_PINS_NUM]; + bool enable_qinq; }; struct ocelot_policer { -- 2.17.1