diff mbox series

[v6,3/3] net: dsa: ocelot: Add support for QinQ Operation

Message ID 20200916094845.10782-4-hongbo.wang@nxp.com
State Changes Requested
Delegated to: David Miller
Headers show
Series Add 802.1AD protocol support for dsa switch and ocelot driver | expand

Commit Message

Hongbo Wang Sept. 16, 2020, 9:48 a.m. UTC
From: "hongbo.wang" <hongbo.wang@nxp.com>

This feature can be test in the following case:
Customer <-----> swp0  <-----> swp1 <-----> ISP

Customer will send and receive packets with single VLAN tag(CTAG),
ISP 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.
devlink dev param set pci/0000:00:00.5 name qinq_port_bitmap \
value 2 cmode runtime
2.
ip link add dev br0 type bridge vlan_protocol 802.1ad
ip link set dev swp0 master br0
ip link set dev swp1 master br0
ip link set dev br0 type bridge vlan_filtering 1
3.
bridge vlan del dev swp0 vid 1 pvid
bridge vlan add dev swp0 vid 100 pvid untagged
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)
ISP(tpid:88A8 vid:100 tpid:8100 vid:222) -> swp1 -> swp0 ->\
            Customer(tpid:8100 vid:222)

Signed-off-by: hongbo.wang <hongbo.wang@nxp.com>
---
 drivers/net/dsa/ocelot/felix.c     | 123 +++++++++++++++++++++++++++++
 drivers/net/ethernet/mscc/ocelot.c |  39 +++++++--
 include/soc/mscc/ocelot.h          |   4 +
 3 files changed, 160 insertions(+), 6 deletions(-)

Comments

Vladimir Oltean Sept. 16, 2020, 10 a.m. UTC | #1
Hi Hongbo,

On Wed, Sep 16, 2020 at 05:48:45PM +0800, hongbo.wang@nxp.com wrote:
> From: "hongbo.wang" <hongbo.wang@nxp.com>
>
> This feature can be test in the following case:
> Customer <-----> swp0  <-----> swp1 <-----> ISP
>
> Customer will send and receive packets with single VLAN tag(CTAG),
> ISP 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.
> devlink dev param set pci/0000:00:00.5 name qinq_port_bitmap \
> value 2 cmode runtime
> 2.
> ip link add dev br0 type bridge vlan_protocol 802.1ad
> ip link set dev swp0 master br0
> ip link set dev swp1 master br0
> ip link set dev br0 type bridge vlan_filtering 1
> 3.
> bridge vlan del dev swp0 vid 1 pvid
> bridge vlan add dev swp0 vid 100 pvid untagged
> 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)
> ISP(tpid:88A8 vid:100 tpid:8100 vid:222) -> swp1 -> swp0 ->\
>             Customer(tpid:8100 vid:222)
>
> Signed-off-by: hongbo.wang <hongbo.wang@nxp.com>
> ---

Can you please explain what is the purpose of the devlink parameter
command? As far as I understand, the commands from step 2 and 3 should
behave like that, even without running the command at step 1.

Thanks,
-Vladimir
Hongbo Wang Sept. 16, 2020, 10:28 a.m. UTC | #2
Hi Vladimir,

> -----Original Message-----
> From: Vladimir Oltean <olteanv@gmail.com>
> Sent: 2020年9月16日 18:00
> To: Hongbo Wang <hongbo.wang@nxp.com>
> Cc: Xiaoliang Yang <xiaoliang.yang_1@nxp.com>; Po Liu <po.liu@nxp.com>;
> Mingkai Hu <mingkai.hu@nxp.com>; allan.nielsen@microchip.com; Claudiu
> Manoil <claudiu.manoil@nxp.com>; Alexandru Marginean
> <alexandru.marginean@nxp.com>; Vladimir Oltean
> <vladimir.oltean@nxp.com>; Leo Li <leoyang.li@nxp.com>; andrew@lunn.ch;
> f.fainelli@gmail.com; vivien.didelot@gmail.com; davem@davemloft.net;
> jiri@resnulli.us; idosch@idosch.org; kuba@kernel.org;
> vinicius.gomes@intel.com; nikolay@cumulusnetworks.com;
> roopa@cumulusnetworks.com; netdev@vger.kernel.org;
> linux-kernel@vger.kernel.org; horatiu.vultur@microchip.com;
> alexandre.belloni@bootlin.com; UNGLinuxDriver@microchip.com;
> ivecera@redhat.com
> Subject: [EXT] Re: [PATCH v6 3/3] net: dsa: ocelot: Add support for QinQ
> Operation
> 
> Caution: EXT Email
> 
> Hi Hongbo,
> 
> On Wed, Sep 16, 2020 at 05:48:45PM +0800, hongbo.wang@nxp.com wrote:
> > From: "hongbo.wang" <hongbo.wang@nxp.com>
> >
> > This feature can be test in the following case:
> > Customer <-----> swp0  <-----> swp1 <-----> ISP
> >
> > Customer will send and receive packets with single VLAN tag(CTAG), ISP
> > 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.
> > devlink dev param set pci/0000:00:00.5 name qinq_port_bitmap \ value 2
> > cmode runtime 2.
> > ip link add dev br0 type bridge vlan_protocol 802.1ad ip link set dev
> > swp0 master br0 ip link set dev swp1 master br0 ip link set dev br0
> > type bridge vlan_filtering 1 3.
> > bridge vlan del dev swp0 vid 1 pvid
> > bridge vlan add dev swp0 vid 100 pvid untagged 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)
> > ISP(tpid:88A8 vid:100 tpid:8100 vid:222) -> swp1 -> swp0 ->\
> >             Customer(tpid:8100 vid:222)
> >
> > Signed-off-by: hongbo.wang <hongbo.wang@nxp.com>
> > ---
> 
> Can you please explain what is the purpose of the devlink parameter command?
> As far as I understand, the commands from step 2 and 3 should behave like
> that, even without running the command at step 1.

if swp0 connects with customer, and swp1 connects with ISP, According to the VSC99599_1_00_TS.pdf,
swp0 and swp1 will have different VLAN_POP_CNT && VLAN_AWARE_ENA, 

swp0 should set VLAN_CFG.VLAN_POP_CNT=0 && VLAN_CFG.VLAN_AWARE_ENA=0
swp1 should set VLAN_CFG.VLAN_POP_CNT=1 && VLAN_CFG.VLAN_AWARE_ENA=1

but when set vlan_filter=1, current code will set same value for both swp0 and swp1, 
for compatibility with existing code(802.1Q mode), so add devlink to set swp0 and swp1 into different modes.

Thanks,
hongbo
Vladimir Oltean Sept. 16, 2020, 10:45 a.m. UTC | #3
On Wed, Sep 16, 2020 at 10:28:38AM +0000, Hongbo Wang wrote:
> Hi Vladimir,
>
> if swp0 connects with customer, and swp1 connects with ISP, According
> to the VSC99599_1_00_TS.pdf, swp0 and swp1 will have different
> VLAN_POP_CNT && VLAN_AWARE_ENA,
>
> swp0 should set VLAN_CFG.VLAN_POP_CNT=0 && VLAN_CFG.VLAN_AWARE_ENA=0
> swp1 should set VLAN_CFG.VLAN_POP_CNT=1 && VLAN_CFG.VLAN_AWARE_ENA=1
>
> but when set vlan_filter=1, current code will set same value for both
> swp0 and swp1, for compatibility with existing code(802.1Q mode), so
> add devlink to set swp0 and swp1 into different modes.

But if you make VLAN_CFG.VLAN_AWARE_ENA=0, does that mean the switch
will accept any 802.1ad VLAN, not only those configured in the VLAN
database of the bridge? Otherwise said, after running the commands
above, and I send a packet to swp0 having tpid:88A8 vid:101, then the
bridge should not accept it.

I might be wrong, but I thought that an 802.1ad bridge with
vlan_filtering=1 behaves the same as an 802.1q bridge, except that it
should filter VLANs using a different TPID (0x88a8 instead of 0x8100).
I don't think the driver, in the way you're configuring it, does that,
does it?

Thanks,
-Vladimir
Hongbo Wang Sept. 17, 2020, 2:37 a.m. UTC | #4
> On Wed, Sep 16, 2020 at 10:28:38AM +0000, Hongbo Wang wrote:
> > Hi Vladimir,
> >
> > if swp0 connects with customer, and swp1 connects with ISP, According
> > to the VSC99599_1_00_TS.pdf, swp0 and swp1 will have different
> > VLAN_POP_CNT && VLAN_AWARE_ENA,
> >
> > swp0 should set VLAN_CFG.VLAN_POP_CNT=0 &&
> VLAN_CFG.VLAN_AWARE_ENA=0
> > swp1 should set VLAN_CFG.VLAN_POP_CNT=1 &&
> VLAN_CFG.VLAN_AWARE_ENA=1
> >
> > but when set vlan_filter=1, current code will set same value for both
> > swp0 and swp1, for compatibility with existing code(802.1Q mode), so
> > add devlink to set swp0 and swp1 into different modes.
> 
> But if you make VLAN_CFG.VLAN_AWARE_ENA=0, does that mean the switch
> will accept any 802.1ad VLAN, not only those configured in the VLAN database
> of the bridge? Otherwise said, after running the commands above, and I send a
> packet to swp0 having tpid:88A8 vid:101, then the bridge should not accept it.
> 
> I might be wrong, but I thought that an 802.1ad bridge with
> vlan_filtering=1 behaves the same as an 802.1q bridge, except that it should
> filter VLANs using a different TPID (0x88a8 instead of 0x8100).
> I don't think the driver, in the way you're configuring it, does that, does it?

hi Vladimir,
you can refer to "4.3.3.0.1 MAN Access Switch Example" in VSC99599_1_00_TS.pdf,
By testing the case, if don't set VLAN_AWARE_ENA=0 for customer's port swp0,
the Q-in-Q feature can't work well.

In order to distinguish the port for customer and for ISP, I add devlink command,
Actually, I can modify the driver config directly, not using devlink, but it will be not compatible with current 
code and user guide. 

Thanks,
hongbo
Vladimir Oltean Oct. 9, 2020, 12:29 p.m. UTC | #5
Hi Hongbo,

On Thu, Sep 17, 2020 at 02:37:59AM +0000, Hongbo Wang wrote:
> > On Wed, Sep 16, 2020 at 10:28:38AM +0000, Hongbo Wang wrote:
> > > Hi Vladimir,
> > >
> > > if swp0 connects with customer, and swp1 connects with ISP, According
> > > to the VSC99599_1_00_TS.pdf, swp0 and swp1 will have different
> > > VLAN_POP_CNT && VLAN_AWARE_ENA,
> > >
> > > swp0 should set VLAN_CFG.VLAN_POP_CNT=0 &&
> > VLAN_CFG.VLAN_AWARE_ENA=0
> > > swp1 should set VLAN_CFG.VLAN_POP_CNT=1 &&
> > VLAN_CFG.VLAN_AWARE_ENA=1
> > >
> > > but when set vlan_filter=1, current code will set same value for both
> > > swp0 and swp1, for compatibility with existing code(802.1Q mode), so
> > > add devlink to set swp0 and swp1 into different modes.
> >
> > But if you make VLAN_CFG.VLAN_AWARE_ENA=0, does that mean the switch
> > will accept any 802.1ad VLAN, not only those configured in the VLAN database
> > of the bridge? Otherwise said, after running the commands above, and I send a
> > packet to swp0 having tpid:88A8 vid:101, then the bridge should not accept it.
> >
> > I might be wrong, but I thought that an 802.1ad bridge with
> > vlan_filtering=1 behaves the same as an 802.1q bridge, except that it should
> > filter VLANs using a different TPID (0x88a8 instead of 0x8100).
> > I don't think the driver, in the way you're configuring it, does that, does it?
>
> hi Vladimir,
> you can refer to "4.3.3.0.1 MAN Access Switch Example" in VSC99599_1_00_TS.pdf,
> By testing the case, if don't set VLAN_AWARE_ENA=0 for customer's port swp0,
> the Q-in-Q feature can't work well.
>
> In order to distinguish the port for customer and for ISP, I add devlink command,
> Actually, I can modify the driver config directly, not using devlink,
> but it will be not compatible with current code and user guide.

I asked this on the Microchip Support portal:

-----------------------------[cut here]-----------------------------

VLAN filtering only on specific TPID
------------------------------------

I would like to configure a port with the following behavior:
- The VLAN table should contain 802.1ad VLANs 1 and 10. VLAN ingress filtering
  should be enabled.
- An untagged frame on ingress should be classified to 802.1ad (TAG_TYPE=1)
  VLAN ID 1 (the port-based VLAN). The frame should be accepted because 802.1ad
  VLAN 1 is in the VLAN table.
- An ingress frame with 802.1Q (0x8100) header VLAN ID 100 should be classified
  to 802.1ad (TAG_TYPE=1) VLAN ID 1 (the port-based VLAN). The frame should be
  accepted because 802.1ad VLAN 1 is in the VLAN table.
- An ingress frame with 802.1ad (0x88a8) header VLAN ID 10 should be classified
  to 802.1ad (TAG_TYPE=1) VLAN ID 10. The frame should be accepted because
  802.1ad VLAN 10 is in the VLAN table.
- An ingress frame with 802.1ad (0x88a8) header VLAN ID 100 should be
  classified to 802.1ad (TAG_TYPE=1) VLAN ID 100. The frame should be dropped
  because 802.1ad VLAN 100 is not in the VLAN table.
How do I configure the switch to obtain this behavior? This is not what the
"Provider Bridges and Q-in-Q Operation" chapter in the reference manual is
explaining how to do. Instead, that chapter suggests to make
VLAN_CFG.VLAN_AWARE_ENA = 0. But I don't want to do this, because I need to be
able to drop the frames with 802.1ad VLAN ID 100 in the example above.

-----------------------------[cut here]-----------------------------

Judging from the fact that I received no answer whatsoever, I can only
deduce that offloading an 8021ad bridge, at least one that has the
semantics that Toshiaki Makita described here,
https://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next.git/commit/?id=1a0b20b257326523ec2a6cb51dd6f26ef179eb84
is not possible with this hardware.

So I think there's little left to do here.

If it helps, I am fairly certain that the sja1105 can offer the
requested services, if you play a little bit with the TPID and TPID2
values. Maybe that's a path forward for your patches, if you still want
to add the generic support in switchdev and in DSA.

Thanks,
-Vladimir
Hongbo Wang Oct. 10, 2020, 3:22 a.m. UTC | #6
Hi Vladimir,

> 
> I asked this on the Microchip Support portal:
> 
> -----------------------------[cut here]-----------------------------
> 
> VLAN filtering only on specific TPID
> ------------------------------------
> 
> I would like to configure a port with the following behavior:
> - The VLAN table should contain 802.1ad VLANs 1 and 10. VLAN ingress
> filtering
>   should be enabled.
> - An untagged frame on ingress should be classified to 802.1ad (TAG_TYPE=1)
>   VLAN ID 1 (the port-based VLAN). The frame should be accepted because
> 802.1ad
>   VLAN 1 is in the VLAN table.
> - An ingress frame with 802.1Q (0x8100) header VLAN ID 100 should be
> classified
>   to 802.1ad (TAG_TYPE=1) VLAN ID 1 (the port-based VLAN). The frame
> should be
>   accepted because 802.1ad VLAN 1 is in the VLAN table.
> - An ingress frame with 802.1ad (0x88a8) header VLAN ID 10 should be
> classified
>   to 802.1ad (TAG_TYPE=1) VLAN ID 10. The frame should be accepted
> because
>   802.1ad VLAN 10 is in the VLAN table.
> - An ingress frame with 802.1ad (0x88a8) header VLAN ID 100 should be
>   classified to 802.1ad (TAG_TYPE=1) VLAN ID 100. The frame should be
> dropped
>   because 802.1ad VLAN 100 is not in the VLAN table.
> How do I configure the switch to obtain this behavior? This is not what the
> "Provider Bridges and Q-in-Q Operation" chapter in the reference manual is
> explaining how to do. Instead, that chapter suggests to make
> VLAN_CFG.VLAN_AWARE_ENA = 0. But I don't want to do this, because I need
> to be able to drop the frames with 802.1ad VLAN ID 100 in the example above.
> 
> -----------------------------[cut here]-----------------------------
> 
> Judging from the fact that I received no answer whatsoever, I can only deduce
> that offloading an 8021ad bridge, at least one that has the semantics that
> Toshiaki Makita described here,
> https://eur01.safelinks.protection.outlook.com/?url=https%3A%2F%2Fgit.kern
> el.org%2Fpub%2Fscm%2Flinux%2Fkernel%2Fgit%2Fnetdev%2Fnet-next.git%2F
> commit%2F%3Fid%3D1a0b20b257326523ec2a6cb51dd6f26ef179eb84&amp;
> data=02%7C01%7Chongbo.wang%40nxp.com%7Cdcfd5e5df4e24455726408d
> 86c4f0493%7C686ea1d3bc2b4c6fa92cd99c5c301635%7C0%7C0%7C6373784
> 33917654330&amp;sdata=kk8WnA0iH9E5yPrLKC8BwSInD6jqm0qGftJ8Jw1Etk
> 8%3D&amp;reserved=0
> is not possible with this hardware.
> 
> So I think there's little left to do here.
> 
> If it helps, I am fairly certain that the sja1105 can offer the requested services,
> if you play a little bit with the TPID and TPID2 values. Maybe that's a path
> forward for your patches, if you still want to add the generic support in
> switchdev and in DSA.
> 

Thanks for your suggestion,
I will research the related code, and will optimize my patches.

Thanks,
hongbo
diff mbox series

Patch

diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c
index a1e1d3824110..5888b0fa5669 100644
--- a/drivers/net/dsa/ocelot/felix.c
+++ b/drivers/net/dsa/ocelot/felix.c
@@ -148,9 +148,26 @@  static void felix_vlan_add(struct dsa_switch *ds, int port,
 				vid, port, err);
 			return;
 		}
+
+		if (vlan->proto == ETH_P_8021AD) {
+			if (!ocelot->qinq_enable) {
+				ocelot->qinq_enable = true;
+				kref_init(&ocelot->qinq_refcount);
+			} else {
+				kref_get(&ocelot->qinq_refcount);
+			}
+		}
 	}
 }
 
+static void felix_vlan_qinq_release(struct kref *ref)
+{
+	struct ocelot *ocelot;
+
+	ocelot = container_of(ref, struct ocelot, qinq_refcount);
+	ocelot->qinq_enable = false;
+}
+
 static int felix_vlan_del(struct dsa_switch *ds, int port,
 			  const struct switchdev_obj_port_vlan *vlan)
 {
@@ -165,6 +182,9 @@  static int felix_vlan_del(struct dsa_switch *ds, int port,
 				vid, port, err);
 			return err;
 		}
+
+		if (ocelot->qinq_enable && vlan->proto == ETH_P_8021AD)
+			kref_put(&ocelot->qinq_refcount, felix_vlan_qinq_release);
 	}
 	return 0;
 }
@@ -173,9 +193,13 @@  static int felix_port_enable(struct dsa_switch *ds, int port,
 			     struct phy_device *phy)
 {
 	struct ocelot *ocelot = ds->priv;
+	struct net_device *slave;
 
 	ocelot_port_enable(ocelot, port, phy);
 
+	slave = dsa_to_port(ds, port)->slave;
+	slave->features |= NETIF_F_HW_VLAN_STAG_FILTER;
+
 	return 0;
 }
 
@@ -555,6 +579,97 @@  static struct ptp_clock_info ocelot_ptp_clock_info = {
 	.enable		= ocelot_ptp_enable,
 };
 
+static int felix_qinq_port_bitmap_get(struct dsa_switch *ds, u32 *bitmap)
+{
+	struct ocelot *ocelot = ds->priv;
+	struct ocelot_port *ocelot_port;
+	int port;
+
+	*bitmap = 0;
+	for (port = 0; port < ds->num_ports; port++) {
+		ocelot_port = ocelot->ports[port];
+		if (ocelot_port->qinq_mode)
+			*bitmap |= 0x01 << port;
+	}
+
+	return 0;
+}
+
+static int felix_qinq_port_bitmap_set(struct dsa_switch *ds, u32 bitmap)
+{
+	struct ocelot *ocelot = ds->priv;
+	struct ocelot_port *ocelot_port;
+	int port;
+
+	for (port = 0; port < ds->num_ports; port++) {
+		ocelot_port = ocelot->ports[port];
+		if (bitmap & (0x01 << port))
+			ocelot_port->qinq_mode = true;
+		else
+			ocelot_port->qinq_mode = false;
+	}
+
+	return 0;
+}
+
+enum felix_devlink_param_id {
+	FELIX_DEVLINK_PARAM_ID_BASE = DEVLINK_PARAM_GENERIC_ID_MAX,
+	FELIX_DEVLINK_PARAM_ID_QINQ_PORT_BITMAP,
+};
+
+static int felix_devlink_param_get(struct dsa_switch *ds, u32 id,
+				   struct devlink_param_gset_ctx *ctx)
+{
+	int err;
+
+	switch (id) {
+	case FELIX_DEVLINK_PARAM_ID_QINQ_PORT_BITMAP:
+		err = felix_qinq_port_bitmap_get(ds, &ctx->val.vu32);
+		break;
+	default:
+		err = -EOPNOTSUPP;
+		break;
+	}
+
+	return err;
+}
+
+static int felix_devlink_param_set(struct dsa_switch *ds, u32 id,
+				   struct devlink_param_gset_ctx *ctx)
+{
+	int err;
+
+	switch (id) {
+	case FELIX_DEVLINK_PARAM_ID_QINQ_PORT_BITMAP:
+		err = felix_qinq_port_bitmap_set(ds, ctx->val.vu32);
+		break;
+	default:
+		err = -EOPNOTSUPP;
+		break;
+	}
+
+	return err;
+}
+
+static const struct devlink_param felix_devlink_params[] = {
+	DSA_DEVLINK_PARAM_DRIVER(FELIX_DEVLINK_PARAM_ID_QINQ_PORT_BITMAP,
+				 "qinq_port_bitmap",
+				 DEVLINK_PARAM_TYPE_U32,
+				 BIT(DEVLINK_PARAM_CMODE_RUNTIME)),
+};
+
+static int felix_setup_devlink_params(struct dsa_switch *ds)
+{
+	return dsa_devlink_params_register(ds, felix_devlink_params,
+					   ARRAY_SIZE(felix_devlink_params));
+}
+
+static void felix_teardown_devlink_params(struct dsa_switch *ds)
+{
+	dsa_devlink_params_unregister(ds, felix_devlink_params,
+				      ARRAY_SIZE(felix_devlink_params));
+}
+
 /* Hardware initialization done here so that we can allocate structures with
  * devm without fear of dsa_register_switch returning -EPROBE_DEFER and causing
  * us to allocate structures twice (leak memory) and map PCI memory twice
@@ -614,6 +729,10 @@  static int felix_setup(struct dsa_switch *ds)
 	ds->mtu_enforcement_ingress = true;
 	ds->configure_vlan_while_not_filtering = true;
 
+	err = felix_setup_devlink_params(ds);
+	if (err < 0)
+		return err;
+
 	return 0;
 }
 
@@ -625,6 +744,8 @@  static void felix_teardown(struct dsa_switch *ds)
 	if (felix->info->mdio_bus_free)
 		felix->info->mdio_bus_free(ocelot);
 
+	felix_teardown_devlink_params(ds);
+
 	ocelot_deinit_timestamp(ocelot);
 	/* stop workqueue thread */
 	ocelot_deinit(ocelot);
@@ -798,6 +919,8 @@  const struct dsa_switch_ops felix_switch_ops = {
 	.cls_flower_del		= felix_cls_flower_del,
 	.cls_flower_stats	= felix_cls_flower_stats,
 	.port_setup_tc          = felix_port_setup_tc,
+	.devlink_param_get	= felix_devlink_param_get,
+	.devlink_param_set	= felix_devlink_param_set,
 };
 
 static int __init felix_init(void)
diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c
index 5abb7d2b0a9e..9186501cef03 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->qinq_enable && 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,18 @@  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->qinq_enable && 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);
+		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 +218,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->qinq_enable && !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 +240,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->qinq_enable && 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..8d0f9f9ec0b2 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,9 @@  struct ocelot {
 	/* Protects the PTP clock */
 	spinlock_t			ptp_clock_lock;
 	struct ptp_pin_desc		ptp_pins[OCELOT_PTP_PINS_NUM];
+
+	bool				qinq_enable;
+	struct kref			qinq_refcount;
 };
 
 struct ocelot_policer {