mxl862xx: add sdk driver mxl862xx
authorJohn Crispin <john@phrozen.org>
Wed, 26 Feb 2025 17:18:05 +0000 (18:18 +0100)
committerJohn Crispin <john@phrozen.org>
Wed, 26 Feb 2025 17:18:05 +0000 (18:18 +0100)
Signed-off-by: John Crispin <john@phrozen.org>
22 files changed:
target/linux/mediatek/dts/mt7988a-openwrt-two.dts [new file with mode: 0644]
target/linux/mediatek/files/drivers/net/dsa/mxl862xx/Kconfig [new file with mode: 0644]
target/linux/mediatek/files/drivers/net/dsa/mxl862xx/Makefile [new file with mode: 0644]
target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_api.c [new file with mode: 0644]
target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_api.h [new file with mode: 0644]
target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_ctp.h [new file with mode: 0644]
target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_flow.h [new file with mode: 0644]
target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_host_api_impl.c [new file with mode: 0644]
target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_host_api_impl.h [new file with mode: 0644]
target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_mdio_relay.c [new file with mode: 0644]
target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_mdio_relay.h [new file with mode: 0644]
target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_mmd_apis.h [new file with mode: 0644]
target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_rmon.h [new file with mode: 0644]
target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_types.h [new file with mode: 0644]
target/linux/mediatek/files/drivers/net/dsa/mxl862xx/mxl862xx.c [new file with mode: 0755]
target/linux/mediatek/files/net/dsa/tag_mxl862xx.c [new file with mode: 0644]
target/linux/mediatek/files/net/dsa/tag_mxl862xx_8021q.c [new file with mode: 0644]
target/linux/mediatek/filogic/base-files/etc/board.d/02_network
target/linux/mediatek/filogic/config-6.6
target/linux/mediatek/image/filogic.mk
target/linux/mediatek/patches-6.6/990-50-smartrg-net-dsa-mxl862xx-add-driver.patch [new file with mode: 0644]
target/linux/mediatek/patches-6.6/990-51-smartrg-net-phy-mxl-gpy-add-mxl862xx-support.patch [new file with mode: 0644]

diff --git a/target/linux/mediatek/dts/mt7988a-openwrt-two.dts b/target/linux/mediatek/dts/mt7988a-openwrt-two.dts
new file mode 100644 (file)
index 0000000..fe2e6ea
--- /dev/null
@@ -0,0 +1,193 @@
+// SPDX-License-Identifier: (GPL-2.0 OR MIT)
+
+/dts-v1/;
+#include "mt7988a-rfb.dts"
+#include <dt-bindings/input/input.h>
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/leds/common.h>
+
+/ {
+       compatible = "openwrt,two", "mediatek,mt7988a";
+       model = "OpenWrt Two";
+
+       aliases {
+               serial0 = &uart0;
+       };
+};
+
+&pio {
+       mdio0_pins: mdio0-pins {
+               mux {
+                       function = "eth";
+                       groups = "mdc_mdio0";
+               };
+
+               conf {
+                       groups = "mdc_mdio0";
+                       drive-strength = <MTK_DRIVE_10mA>;
+               };
+       };
+
+       spic_pins: spi1-pins {
+               mux {
+                       function = "spi";
+                       groups = "spi1";
+               };
+       };
+};
+
+&mdio_bus {
+       #address-cells = <1>;
+       #size-cells = <0>;
+       reset-gpios = <&pio 72 GPIO_ACTIVE_LOW>;
+       reset-assert-us = <100000>;
+       reset-deassert-us = <100000>;
+
+       phy0: ethernet-phy@0 {
+               reg = <0>;
+               compatible = "ethernet-phy-ieee802.3-c45";
+       };
+
+       phy8: ethernet-phy@8 {
+               reg = <8>;
+               compatible = "ethernet-phy-ieee802.3-c45";
+       };
+
+       switch@10 {
+               #address-cells = <1>;
+               #size-cells = <0>;
+               compatible = "mxl,86252";
+               reg = <0x10>;
+               dsa,member = <1 0>;
+
+               status = "okay";
+
+               reset-gpios = <&pio 1 GPIO_ACTIVE_LOW>;
+               reset-assert-us = <100000>;
+               reset-deassert-us = <1000000>;
+
+               ports {
+                       #address-cells = <1>;
+                       #size-cells = <0>;
+
+                       port@0 {
+                               reg = <0>;
+                               label = "mxl1";
+                               phy-handle = <&swphy0>;
+                               phy-mode = "internal";
+                               status = "okay";
+                       };
+
+                       port@1 {
+                               reg = <1>;
+                               label = "mxl2";
+                               phy-handle = <&swphy1>;
+                               phy-mode = "internal";
+                               status = "okay";
+                       };
+
+                       port@2 {
+                               reg = <2>;
+                               label = "mxl3";
+                               phy-handle = <&swphy2>;
+                               phy-mode = "internal";
+                               status = "okay";
+                       };
+
+                       port@3 {
+                               reg = <3>;
+                               label = "mxl4";
+                               phy-handle = <&swphy3>;
+                               phy-mode = "internal";
+                               status = "okay";
+                       };
+
+                       port@8 {
+                               reg = <8>;
+                               label = "cpu";
+                               phy-mode = "usxgmii";
+                               ethernet = <&gmac2>;
+                               dsa-tag-protocol = "mxl862";
+                       //      dsa-tag-protocol = "mxl862_8021q";
+
+                               fixed-link {
+                                       speed = <10000>;
+                                       full-duplex;
+                               };
+                       };
+
+                       /**
+                        * not supported by MXL DSA driver yet
+                        **
+                       port@9 {
+                               reg = <9>;
+                               label = "lan9";
+                               phy-handle = <&phy0>;
+                               phy-mode = "usxgmii";
+                               status = "okay";
+                       };
+                       */
+               };
+
+               mdio {
+                        #address-cells = <1>;
+                        #size-cells = <0>;
+
+                        swphy0: swphy@0 {
+                                reg = <0>;
+                        };
+                        swphy1: swphy@1 {
+                                reg = <1>;
+                        };
+                        swphy2: swphy@2 {
+                                reg = <2>;
+                        };
+                        swphy3: swphy@3 {
+                                reg = <3>;
+                        };
+                };
+       };
+};
+
+&gmac0 {
+};
+
+&gmac1 {
+        phy-mode = "10gbase-r";
+
+        status = "okay";
+
+        fixed-link {
+                speed = <10000>;
+                full-duplex;
+                pause;
+        };
+};
+
+&gmac2 {
+        label = "dsa1";
+        phy-mode = "10gbase-r";
+
+        status = "okay";
+
+        fixed-link {
+                speed = <10000>;
+                full-duplex;
+                pause;
+        };
+};
+
+&uart1 {
+       status = "okay";
+};
+
+&spi1 {
+       pinctrl-names = "default";
+       /* pin shared with snfi */
+       pinctrl-0 = <&spic_pins>;
+       status = "disabled";
+};
+
+&pcie0 {
+       status = "okay";
+};
diff --git a/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/Kconfig b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/Kconfig
new file mode 100644 (file)
index 0000000..5b4e051
--- /dev/null
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
+config NET_DSA_MXL862
+       tristate "MaxLinear MxL862xx"
+       depends on NET_DSA
+       select NET_DSA_TAG_MXL862
+       help
+         This enables support for the MaxLinear MxL862xx switch family.
diff --git a/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/Makefile b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/Makefile
new file mode 100644 (file)
index 0000000..62473cd
--- /dev/null
@@ -0,0 +1,3 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_NET_DSA_MXL862) += mxl862xx_dsa.o
+mxl862xx_dsa-y := mxl862xx.o host_api/mxl862xx_api.o host_api/mxl862xx_host_api_impl.o host_api/mxl862xx_mdio_relay.o
diff --git a/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_api.c b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_api.c
new file mode 100644 (file)
index 0000000..92a74df
--- /dev/null
@@ -0,0 +1,272 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * drivers/net/dsa/host_api/mxl862xx_api.c - dsa driver for Maxlinear mxl862xx switch chips family
+ *
+ * Copyright (C) 2024 MaxLinear Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#include "mxl862xx_host_api_impl.h"
+#include "mxl862xx_api.h"
+#include "mxl862xx_mmd_apis.h"
+
+int sys_misc_fw_version(const mxl862xx_device_t *dev,
+                       struct sys_fw_image_version *sys_img_ver)
+{
+       return mxl862xx_api_wrap(dev, SYS_MISC_FW_VERSION, sys_img_ver,
+                           sizeof(*sys_img_ver), 0, sizeof(*sys_img_ver));
+}
+
+int mxl862xx_register_mod(const mxl862xx_device_t *dev, mxl862xx_register_mod_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_COMMON_REGISTERMOD, parm, sizeof(*parm), 0,
+                           0);
+}
+
+int mxl862xx_port_link_cfg_set(const mxl862xx_device_t *dev, mxl862xx_port_link_cfg_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_COMMON_PORTLINKCFGSET, parm, sizeof(*parm),
+                           MXL862XX_COMMON_PORTLINKCFGGET, 0);
+}
+
+int mxl862xx_port_cfg_get(const mxl862xx_device_t *dev, mxl862xx_port_cfg_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_COMMON_PORTCFGGET, parm, sizeof(*parm), 0,
+                           sizeof(*parm));
+}
+
+int mxl862xx_port_cfg_set(const mxl862xx_device_t *dev, mxl862xx_port_cfg_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_COMMON_PORTCFGSET, parm, sizeof(*parm),
+                           MXL862XX_COMMON_PORTCFGGET, 0);
+}
+
+int mxl862xx_bridge_alloc(const mxl862xx_device_t *dev, mxl862xx_bridge_alloc_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_BRIDGE_ALLOC, parm, sizeof(*parm), 0,
+                           sizeof(*parm));
+}
+int mxl862xx_bridge_free(const mxl862xx_device_t *dev, mxl862xx_bridge_alloc_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_BRIDGE_FREE, parm, sizeof(*parm), 0, 0);
+}
+
+int mxl862xx_bridge_config_set(const mxl862xx_device_t *dev, mxl862xx_bridge_config_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_BRIDGE_CONFIGSET, parm, sizeof(*parm),
+                           MXL862XX_BRIDGE_CONFIGGET, 0);
+}
+
+int mxl862xx_bridge_config_get(const mxl862xx_device_t *dev, mxl862xx_bridge_config_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_BRIDGE_CONFIGGET, parm, sizeof(*parm), 0,
+                           sizeof(*parm));
+}
+
+int mxl862xx_bridge_port_alloc(const mxl862xx_device_t *dev, mxl862xx_bridge_port_alloc_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_BRIDGEPORT_ALLOC, parm, sizeof(*parm), 0,
+                           sizeof(*parm));
+}
+
+int mxl862xx_bridge_port_free(const mxl862xx_device_t *dev, mxl862xx_bridge_port_alloc_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_BRIDGEPORT_FREE, parm, sizeof(*parm), 0, 0);
+}
+
+int mxl862xx_bridge_port_config_set(const mxl862xx_device_t *dev,
+                                   mxl862xx_bridge_port_config_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_BRIDGEPORT_CONFIGSET, parm, sizeof(*parm),
+                           MXL862XX_BRIDGEPORT_CONFIGGET, 0);
+}
+
+int mxl862xx_bridge_port_config_get(const mxl862xx_device_t *dev,
+                                   mxl862xx_bridge_port_config_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_BRIDGEPORT_CONFIGGET, parm, sizeof(*parm),
+                           0, sizeof(*parm));
+}
+
+int mxl862xx_debug_rmon_port_get(const mxl862xx_device_t *dev,
+                                mxl862xx_debug_rmon_port_cnt_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_DEBUG_RMON_PORT_GET, parm, sizeof(*parm),
+                           0, sizeof(*parm));
+}
+
+int mxl862xx_mac_table_clear(const mxl862xx_device_t *dev)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_MAC_TABLECLEAR, NULL, 0, 0, 0);
+}
+
+int mxl862xx_mac_table_clear_cond(const mxl862xx_device_t *dev,
+                                 mxl862xx_mac_table_clear_cond_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_MAC_TABLECLEARCOND, parm, sizeof(*parm), 0,
+                           0);
+}
+
+int mxl862xx_mac_table_entry_read(const mxl862xx_device_t *dev,
+                                 mxl862xx_mac_table_read_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_MAC_TABLEENTRYREAD, parm, sizeof(*parm), 0,
+                           sizeof(*parm));
+}
+
+int mxl862xx_mac_table_entry_add(const mxl862xx_device_t *dev,
+                                mxl862xx_mac_table_add_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_MAC_TABLEENTRYADD, parm, sizeof(*parm), 0,
+                           0);
+}
+
+int mxl862xx_mac_table_entry_remove(const mxl862xx_device_t *dev,
+                                   mxl862xx_mac_table_remove_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_MAC_TABLEENTRYREMOVE, parm, sizeof(*parm),
+                           0, 0);
+}
+
+int mxl862xx_stp_port_cfg_set(const mxl862xx_device_t *dev, mxl862xx_stp_port_cfg_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_STP_PORTCFGSET, parm, sizeof(*parm),
+                           MXL862XX_STP_PORTCFGGET, 0);
+}
+
+int mxl862xx_ss_sp_tag_get(const mxl862xx_device_t *dev, mxl862xx_ss_sp_tag_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_SS_SPTAG_GET, parm, sizeof(*parm), 0,
+                           sizeof(*parm));
+}
+
+int mxl862xx_ss_sp_tag_set(const mxl862xx_device_t *dev, mxl862xx_ss_sp_tag_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_SS_SPTAG_SET, parm, sizeof(*parm),
+                           MXL862XX_SS_SPTAG_GET, sizeof(*parm));
+}
+
+int mxl862xx_ctp_port_config_get(const mxl862xx_device_t *dev,
+                                mxl862xx_ctp_port_config_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_CTP_PORTCONFIGGET, parm, sizeof(*parm), 0,
+                           sizeof(*parm));
+}
+
+int mxl862xx_ctp_port_config_set(const mxl862xx_device_t *dev,
+                                mxl862xx_ctp_port_config_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_CTP_PORTCONFIGSET, parm, sizeof(*parm),
+                           MXL862XX_CTP_PORTCONFIGGET, 0);
+}
+
+int mxl862xx_ctp_port_assignment_set(const mxl862xx_device_t *dev,
+                                    mxl862xx_ctp_port_assignment_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_CTP_PORTASSIGNMENTSET, parm, sizeof(*parm),
+                           MXL862XX_CTP_PORTASSIGNMENTGET, 0);
+}
+
+int mxl862xx_ctp_port_assignment_get(const mxl862xx_device_t *dev,
+                                    mxl862xx_ctp_port_assignment_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_CTP_PORTASSIGNMENTGET, parm, sizeof(*parm),
+                           0, sizeof(*parm));
+}
+
+int mxl862xx_monitor_port_cfg_get(const mxl862xx_device_t *dev,
+                                 mxl862xx_monitor_port_cfg_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_COMMON_MONITORPORTCFGGET, parm,
+                           sizeof(*parm), 0, sizeof(*parm));
+}
+
+int mxl862xx_monitor_port_cfg_set(const mxl862xx_device_t *dev,
+                                 mxl862xx_monitor_port_cfg_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_COMMON_MONITORPORTCFGSET, parm,
+                           sizeof(*parm), MXL862XX_COMMON_MONITORPORTCFGGET, 0);
+}
+
+int mxl862xx_extended_vlan_alloc(const mxl862xx_device_t *dev,
+                                mxl862xx_extendedvlan_alloc_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_EXTENDEDVLAN_ALLOC, parm, sizeof(*parm), 0,
+                           sizeof(*parm));
+}
+
+int mxl862xx_extended_vlan_set(const mxl862xx_device_t *dev,
+                              mxl862xx_extendedvlan_config_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_EXTENDEDVLAN_SET, parm, sizeof(*parm),
+                           MXL862XX_EXTENDEDVLAN_GET, 0);
+}
+
+int mxl862xx_extended_vlan_get(const mxl862xx_device_t *dev,
+                              mxl862xx_extendedvlan_config_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_EXTENDEDVLAN_GET, parm, sizeof(*parm), 0,
+                           sizeof(*parm));
+}
+
+int mxl862xx_extended_vlan_free(const mxl862xx_device_t *dev,
+                               mxl862xx_extendedvlan_alloc_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_EXTENDEDVLAN_FREE, parm, sizeof(*parm), 0,
+                           0);
+}
+
+int mxl862xx_vlan_filter_alloc(const mxl862xx_device_t *dev,
+                              mxl862xx_vlanfilter_alloc_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_VLANFILTER_ALLOC, parm, sizeof(*parm), 0,
+                           sizeof(*parm));
+}
+
+int mxl862xx_vlan_filter_set(const mxl862xx_device_t *dev,
+                            mxl862xx_vlanfilter_config_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_VLANFILTER_SET, parm, sizeof(*parm),
+                           MXL862XX_VLANFILTER_GET, 0);
+}
+
+int mxl862xx_vlan_filter_get(const mxl862xx_device_t *dev,
+                            mxl862xx_vlanfilter_config_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_VLANFILTER_GET, parm, sizeof(*parm), 0,
+                           sizeof(*parm));
+}
+
+int mxl862xx_vlan_filter_free(const mxl862xx_device_t *dev,
+                             mxl862xx_vlanfilter_alloc_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_VLANFILTER_FREE, parm, sizeof(*parm), 0,
+                           0);
+}
+
+int mxl862xx_cfg_get(const mxl862xx_device_t *dev, mxl862xx_cfg_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_COMMON_CFGGET, parm, sizeof(*parm), 0,
+                           sizeof(*parm));
+}
+
+int mxl862xx_cfg_set(const mxl862xx_device_t *dev, mxl862xx_cfg_t *parm)
+{
+       return mxl862xx_api_wrap(dev, MXL862XX_COMMON_CFGSET, parm, sizeof(*parm),
+                               MXL862XX_COMMON_CFGGET, 0);
+}
+
diff --git a/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_api.h b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_api.h
new file mode 100644 (file)
index 0000000..d222952
--- /dev/null
@@ -0,0 +1,1826 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * drivers/net/dsa/host_api/mxl862xx_api.h - DSA Driver for MaxLinear Mxl862xx switch chips family
+ *
+ * Copyright (C) 2024 MaxLinear Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#ifndef _MXL862XX_API_H_
+#define _MXL862XX_API_H_
+
+#include "mxl862xx_types.h"
+#include "mxl862xx_rmon.h"
+
+#define MxL862XX_SDMA_PCTRLp(p) (0xBC0 + ((p) * 0x6))
+#define MXL862XX_SDMA_PCTRL_EN BIT(0) /* SDMA Port Enable */
+
+/* Ethernet Switch Fetch DMA Port Control Register */
+#define MxL862XX_FDMA_PCTRLp(p) (0xA80 + ((p) * 0x6))
+#define MXL862XX_FDMA_PCTRL_EN BIT(0) /* FDMA Port Enable */
+
+#pragma pack(push, 1)
+#pragma scalar_storage_order little-endian
+
+//#define SCK_FW_1_0_41
+
+/** \brief Spanning Tree Protocol port states.
+    Used by \ref MXL862XX_STP_port_cfg_t. */
+typedef enum {
+       /** Forwarding state. The port is allowed to transmit and receive
+           all packets. Address Learning is allowed. */
+       MXL862XX_STP_PORT_STATE_FORWARD = 0,
+       /** Disabled/Discarding state. The port entity will not transmit
+           and receive any packets. Learning is disabled in this state. */
+       MXL862XX_STP_PORT_STATE_DISABLE = 1,
+       /** Learning state. The port entity will only transmit and receive
+           Spanning Tree Protocol packets (BPDU). All other packets are discarded.
+           MAC table address learning is enabled for all good frames. */
+       MXL862XX_STP_PORT_STATE_LEARNING = 2,
+       /** Blocking/Listening. Only the Spanning Tree Protocol packets will
+           be received and transmitted. All other packets are discarded by
+           the port entity. MAC table address learning is disabled in this
+           state. */
+       MXL862XX_STP_PORT_STATE_BLOCKING = 3
+} mxl862xx_stp_port_state_t;
+
+/** \brief Bridge Port Allocation.
+    Used by \ref MXL862XX_BRIDGE_PORT_ALLOC and \ref MXL862XX_BRIDGE_PORT_FREE. */
+typedef struct {
+       /** If \ref MXL862XX_BRIDGE_PORT_ALLOC is successful, a valid ID will be returned
+           in this field. Otherwise, \ref INVALID_HANDLE is returned in this field.
+           For \ref MXL862XX_BRIDGE_PORT_FREE, this field should be valid ID returned by
+           \ref MXL862XX_BRIDGE_PORT_ALLOC. ID 0 is special for CPU port in PRX300
+           by mapping to CTP 0 (Logical Port 0 with Sub-interface ID 0), and
+           pre-alloced during initialization. */
+       u16 bridge_port_id;
+} mxl862xx_bridge_port_alloc_t;
+
+/** \brief Color Remarking Mode
+    Used by \ref MXL862XX_CTP_port_config_t. */
+typedef enum {
+       /** values from last process stage */
+       MXL862XX_REMARKING_NONE = 0,
+       /** DEI mark mode */
+       MXL862XX_REMARKING_DEI = 2,
+       /** PCP 8P0D mark mode */
+       MXL862XX_REMARKING_PCP_8P0D = 3,
+       /** PCP 7P1D mark mode */
+       MXL862XX_REMARKING_PCP_7P1D = 4,
+       /** PCP 6P2D mark mode */
+       MXL862XX_REMARKING_PCP_6P2D = 5,
+       /** PCP 5P3D mark mode */
+       MXL862XX_REMARKING_PCP_5P3D = 6,
+       /** DSCP AF class */
+       MXL862XX_REMARKING_DSCP_AF = 7
+} mxl862xx_color_remarking_mode_t;
+
+/** \brief Meters for various egress traffic type.
+    Used by \ref MXL862XX_BRIDGE_port_config_t. */
+typedef enum {
+       /** Index of broadcast traffic meter */
+       MXL862XX_BRIDGE_PORT_EGRESS_METER_BROADCAST = 0,
+       /** Index of known multicast traffic meter */
+       MXL862XX_BRIDGE_PORT_EGRESS_METER_MULTICAST = 1,
+       /** Index of unknown multicast IP traffic meter */
+       MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_IP = 2,
+       /** Index of unknown multicast non-IP traffic meter */
+       MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_NON_IP = 3,
+       /** Index of unknown unicast traffic meter */
+       MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_UC = 4,
+       /** Index of traffic meter for other types */
+       MXL862XX_BRIDGE_PORT_EGRESS_METER_OTHERS = 5,
+       /** Number of index */
+       MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX = 6
+} mxl862xx_bridge_port_egress_meter_t;
+
+/** \brief P-mapper Mapping Mode
+    Used by \ref MXL862XX_CTP_port_config_t. */
+typedef enum {
+       /** Use PCP for VLAN tagged packets to derive sub interface ID group.
+
+           \remarks
+           P-mapper table entry 1-8. */
+       MXL862XX_PMAPPER_MAPPING_PCP = 0,
+       /** Use LAG Index for Pmapper access (regardless of IP and VLAN packet)to
+           derive sub interface ID group.
+
+           \remarks
+           P-mapper table entry 9-72. */
+       MXL862XX_PMAPPER_MAPPING_LAG = 1,
+       /** Use DSCP for VLAN tagged IP packets to derive sub interface ID group.
+
+           \remarks
+           P-mapper table entry 9-72. */
+       MXL862XX_PMAPPER_MAPPING_DSCP = 2,
+} mxl862xx_pmapper_mapping_mode_t;
+
+/** \brief P-mapper Configuration
+    Used by \ref MXL862XX_CTP_port_config_t, MXL862XX_BRIDGE_port_config_t.
+    In case of LAG, it is user's responsibility to provide the mapped entries
+    in given P-mapper table. In other modes the entries are auto mapped from
+    input packet. */
+typedef struct {
+       /** Index of P-mapper <0-31>. */
+       u16 pmapper_id;
+
+       /** Sub interface ID group.
+
+           \remarks
+           Entry 0 is for non-IP and non-VLAN tagged packets. Entries 1-8 are PCP
+           mapping entries for VLAN tagged packets with \ref MXL862XX_PMAPPER_MAPPING_PCP
+           selected. Entries 9-72 are DSCP or LAG mapping entries for IP packets without
+           VLAN tag or VLAN tagged packets with \ref MXL862XX_PMAPPER_MAPPING_DSCP or
+           MXL862XX_PMAPPER_MAPPING_LAG selected. When LAG is selected this 8bit field is
+           decoded as Destination sub-interface ID group field bits 3:0, Destination
+           logical port ID field bits 7:4 */
+       u8 dest_sub_if_id_group[73];
+} mxl862xx_pmapper_t;
+
+/** \brief Bridge Port configuration mask.
+    Used by \ref MXL862XX_BRIDGE_port_config_t. */
+typedef enum {
+       /** Mask for \ref MXL862XX_BRIDGE_port_config_t::n_bridge_id */
+       MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID = 0x00000001,
+       /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_ingress_extended_vlan_enable and
+           \ref MXL862XX_BRIDGE_port_config_t::n_ingress_extended_vlan_block_id */
+       MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN = 0x00000002,
+       /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_egress_extended_vlan_enable and
+           \ref MXL862XX_BRIDGE_port_config_t::n_egress_extended_vlan_block_id */
+       MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN = 0x00000004,
+       /** Mask for \ref MXL862XX_BRIDGE_port_config_t::e_ingress_marking_mode */
+       MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_MARKING = 0x00000008,
+       /** Mask for \ref MXL862XX_BRIDGE_port_config_t::e_egress_remarking_mode */
+       MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_REMARKING = 0x00000010,
+       /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_ingress_metering_enable and
+           \ref MXL862XX_BRIDGE_port_config_t::n_ingress_traffic_meter_id */
+       MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_METER = 0x00000020,
+       /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_egress_sub_metering_enable and
+           \ref MXL862XX_BRIDGE_port_config_t::n_egress_traffic_sub_meter_id */
+       MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_SUB_METER = 0x00000040,
+       /** Mask for \ref MXL862XX_BRIDGE_port_config_t::n_dest_logical_port_id,
+           \ref MXL862XX_BRIDGE_port_config_t::b_pmapper_enable,
+           \ref MXL862XX_BRIDGE_port_config_t::n_dest_sub_if_id_group,
+           \ref MXL862XX_BRIDGE_port_config_t::e_pmapper_mapping_mode,
+           \ref MXL862XX_BRIDGE_port_config_t::b_pmapper_id_valid and
+           \ref MXL862XX_BRIDGE_port_config_t::s_pmapper. */
+       MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_CTP_MAPPING = 0x00000080,
+       /** Mask for \ref MXL862XX_BRIDGE_port_config_t::n_bridge_port_map */
+       MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP = 0x00000100,
+
+       /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_mc_dest_ip_lookup_enable. */
+       MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_DEST_IP_LOOKUP = 0x00000200,
+       /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_mc_src_ip_lookup_enable. */
+       MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_IP_LOOKUP = 0x00000400,
+       /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_dest_mac_lookup_enable. */
+       MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_DEST_MAC_LOOKUP = 0x00000800,
+       /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_src_mac_learning_enable. */
+       MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING = 0x00001000,
+       /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_mac_spoofing_detect_enable. */
+       MXL862XX_BRIDGE_PORT_CONFIG_MASK_MAC_SPOOFING = 0x00002000,
+       /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_port_lock_enable. */
+       MXL862XX_BRIDGE_PORT_CONFIG_MASK_PORT_LOCK = 0x00004000,
+
+       /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_mac_learning_limit_enable and
+           \ref MXL862XX_BRIDGE_port_config_t::n_mac_learning_limit. */
+       MXL862XX_BRIDGE_PORT_CONFIG_MASK_MAC_LEARNING_LIMIT = 0x00008000,
+       /** Mask for \ref MXL862XX_BRIDGE_port_config_t::n_mac_learning_count */
+       MXL862XX_BRIDGE_PORT_CONFIG_MASK_MAC_LEARNED_COUNT = 0x00010000,
+
+       /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_ingress_vlan_filter_enable and
+           \ref MXL862XX_BRIDGE_port_config_t::n_ingress_vlan_filter_block_id. */
+       MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN_FILTER = 0x00020000,
+       /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_bypass_egress_vlan_filter1,
+           \ref MXL862XX_BRIDGE_port_config_t::b_egress_vlan_filter1Enable
+           and \ref MXL862XX_BRIDGE_port_config_t::n_egress_vlan_filter1Block_id. */
+       MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN_FILTER1 = 0x00040000,
+       /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_egress_vlan_filter2Enable and
+           \ref MXL862XX_BRIDGE_port_config_t::n_egress_vlan_filter2Block_id. */
+       MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN_FILTER2 = 0x00080000,
+       /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_vlan_tag_selection,
+           \ref MXL862XX_BRIDGE_port_config_t::b_vlan_src_mac_priority_enable,
+           \ref MXL862XX_BRIDGE_port_config_t::b_vlan_src_mac_dEIEnable,
+           \ref MXL862XX_BRIDGE_port_config_t::b_vlan_src_mac_vid_enable,
+           \ref MXL862XX_BRIDGE_port_config_t::b_vlan_dst_mac_priority_enable,
+           \ref MXL862XX_BRIDGE_port_config_t::b_vlan_dst_mac_dEIEnable,
+           \ref MXL862XX_BRIDGE_port_config_t::b_vlan_dst_mac_vid_enable */
+       MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MAC_LEARNING = 0x00100000,
+       /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_vlan_multicast_priority_enable,
+           \ref MXL862XX_BRIDGE_port_config_t::b_vlan_multicast_dEIEnable,
+           \ref MXL862XX_BRIDGE_port_config_t::b_vlan_multicast_vid_enable */
+       MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MULTICAST_LOOKUP = 0x00200000,
+       /** Mask for \ref MXL862XX_BRIDGE_port_config_t::n_loop_violation_count */
+       MXL862XX_BRIDGE_PORT_CONFIG_MASK_LOOP_VIOLATION_COUNTER = 0x00400000,
+       /** Enable all */
+       MXL862XX_BRIDGE_PORT_CONFIG_MASK_ALL = 0x7FFFFFFF,
+       /** Bypass any check for debug purpose */
+       MXL862XX_BRIDGE_PORT_CONFIG_MASK_FORCE = 0x80000000
+} mxl862xx_bridge_port_config_mask_t;
+
+/** \brief Color Marking Mode
+    Used by \ref MXL862XX_CTP_port_config_t. */
+typedef enum {
+       /** mark packets (except critical) to green */
+       MXL862XX_MARKING_ALL_GREEN = 0,
+       /** do not change color and priority */
+       MXL862XX_MARKING_INTERNAL_MARKING = 1,
+       /** DEI mark mode */
+       MXL862XX_MARKING_DEI = 2,
+       /** PCP 8P0D mark mode */
+       MXL862XX_MARKING_PCP_8P0D = 3,
+       /** PCP 7P1D mark mode */
+       MXL862XX_MARKING_PCP_7P1D = 4,
+       /** PCP 6P2D mark mode */
+       MXL862XX_MARKING_PCP_6P2D = 5,
+       /** PCP 5P3D mark mode */
+       MXL862XX_MARKING_PCP_5P3D = 6,
+       /** DSCP AF class */
+       MXL862XX_MARKING_DSCP_AF = 7
+} mxl862xx_color_marking_mode_t;
+
+/** \brief Bridge Port Configuration.
+    Used by \ref MXL862XX_BRIDGE_PORT_CONFIG_SET and \ref MXL862XX_BRIDGE_PORT_CONFIG_GET. */
+typedef struct {
+       /** Bridge Port ID allocated by \ref MXL862XX_BRIDGE_PORT_ALLOC.
+
+           \remarks
+           If \ref MXL862XX_BRIDGE_port_config_t::e_mask has
+           \ref MXL862XX_Bridge_port_config_mask_t::MXL862XX_BRIDGE_PORT_CONFIG_MASK_FORCE, this
+           field is absolute index of Bridge Port in hardware for debug purpose,
+           bypassing any check. */
+       u16 bridge_port_id;
+
+       /** Mask for updating/retrieving fields. */
+       mxl862xx_bridge_port_config_mask_t mask;
+
+       /** Bridge ID (FID) to which this bridge port is associated. A default
+           bridge (ID 0) should be always available. */
+       u16 bridge_id;
+
+       /** Enable extended VLAN processing for ingress non-IGMP traffic. */
+       bool ingress_extended_vlan_enable;
+       /** Extended VLAN block allocated for ingress non-IGMP traffic. It defines
+           extended VLAN process for ingress non-IGMP traffic. Valid when
+           b_ingress_extended_vlan_enable is TRUE. */
+       u16 ingress_extended_vlan_block_id;
+       /** Extended VLAN block size for ingress non-IGMP traffic. This is optional.
+           If it is 0, the block size of n_ingress_extended_vlan_block_id will be used.
+           Otherwise, this field will be used. */
+       u16 ingress_extended_vlan_block_size;
+
+       /** Enable extended VLAN processing enabled for egress non-IGMP traffic. */
+       bool egress_extended_vlan_enable;
+       /** Extended VLAN block allocated for egress non-IGMP traffic. It defines
+           extended VLAN process for egress non-IGMP traffic. Valid when
+           b_egress_extended_vlan_enable is TRUE. */
+       u16 egress_extended_vlan_block_id;
+       /** Extended VLAN block size for egress non-IGMP traffic. This is optional.
+           If it is 0, the block size of n_egress_extended_vlan_block_id will be used.
+           Otherwise, this field will be used. */
+       u16 egress_extended_vlan_block_size;
+
+       /** Ingress color marking mode for ingress traffic. */
+       mxl862xx_color_marking_mode_t ingress_marking_mode;
+
+       /** Color remarking for egress traffic. */
+       mxl862xx_color_remarking_mode_t egress_remarking_mode;
+
+       /** Traffic metering on ingress traffic applies. */
+       bool ingress_metering_enable;
+       /** Meter for ingress Bridge Port process.
+
+           \remarks
+           Meter should be allocated with \ref MXL862XX_QOS_METER_ALLOC before Bridge
+           port configuration. If this Bridge port is re-set, the last used meter
+           should be released. */
+       u16 ingress_traffic_meter_id;
+
+       /** Traffic metering on various types of egress traffic (such as broadcast,
+           multicast, unknown unicast, etc) applies. */
+       bool egress_sub_metering_enable[MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX];
+       /** Meter for egress Bridge Port process with specific type (such as
+           broadcast, multicast, unknown unicast, etc). Need pre-allocated for each
+           type. */
+       u16 egress_traffic_sub_meter_id[MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX];
+
+       /** This field defines destination logical port. */
+       u8 dest_logical_port_id;
+       /** This field indicates whether to enable P-mapper. */
+       bool pmapper_enable;
+       /** When b_pmapper_enable is FALSE, this field defines destination sub
+           interface ID group. */
+       u16 dest_sub_if_id_group;
+       /** When b_pmapper_enable is TRUE, this field selects either DSCP or PCP to
+           derive sub interface ID. */
+       mxl862xx_pmapper_mapping_mode_t pmapper_mapping_mode;
+       /** When b_pmapper_enable is TRUE, P-mapper is used. This field determines
+           whether s_pmapper.n_pmapper_id is valid. If this field is TRUE, the
+           P-mapper is re-used and no allocation of new P-mapper or value
+           change in the P-mapper. If this field is FALSE, allocation is
+           taken care in the API implementation. */
+       bool pmapper_id_valid;
+       /** When b_pmapper_enable is TRUE, P-mapper is used. if b_pmapper_id_valid is
+           FALSE, API implementation need take care of P-mapper allocation,
+           and maintain the reference counter of P-mapper used multiple times.
+           If b_pmapper_id_valid is TRUE, only s_pmapper.n_pmapper_id is used to
+           associate the P-mapper, and there is no allocation of new P-mapper
+           or value change in the P-mapper. */
+       mxl862xx_pmapper_t pmapper;
+
+       /** Port map define broadcast domain.
+
+           \remarks
+           Each bit is one bridge port. Bridge port ID is index * 16 + bit offset.
+           For example, bit 1 of n_bridge_port_map[1] is bridge port ID 17. */
+       u16 bridge_port_map[8]; /* max can be 16 */
+
+       /** Multicast IP table is searched if this field is FALSE and traffic is IP
+           multicast. */
+       bool mc_dest_ip_lookup_disable;
+       /** Multicast IP table is searched if this field is TRUE and traffic is IP
+           multicast. */
+       bool mc_src_ip_lookup_enable;
+
+       /** Default is FALSE. Packet is treated as "unknown" if it's not
+           broadcast/multicast packet. */
+       bool dest_mac_lookup_disable;
+
+       /** Default is FALSE. Source MAC address is learned. */
+       bool src_mac_learning_disable;
+
+       /** If this field is TRUE and MAC address which is already learned in another
+           bridge port appears on this bridge port, port locking violation is
+           detected. */
+       bool mac_spoofing_detect_enable;
+
+       /** If this field is TRUE and MAC address which is already learned in this
+           bridge port appears on another bridge port, port locking violation is
+           detected. */
+       bool port_lock_enable;
+
+       /** Enable MAC learning limitation. */
+       bool mac_learning_limit_enable;
+       /** Max number of MAC can be learned from this bridge port. */
+       u16 mac_learning_limit;
+
+       /** Get number of Loop violation counter from this bridge port. */
+       u16 loop_violation_count;
+
+       /** Get number of MAC address learned from this bridge port. */
+       u16 mac_learning_count;
+
+       /** Enable ingress VLAN filter */
+       bool ingress_vlan_filter_enable;
+       /** VLAN filter block of ingress traffic if
+           \ref MXL862XX_BRIDGE_port_config_t::b_ingress_vlan_filter_enable is TRUE. */
+       u16 ingress_vlan_filter_block_id;
+       /** VLAN filter block size. This is optional.
+           If it is 0, the block size of n_ingress_vlan_filter_block_id will be used.
+           Otherwise, this field will be used. */
+       u16 ingress_vlan_filter_block_size;
+       /** For ingress traffic, bypass VLAN filter 1 at egress bridge port
+           processing. */
+       bool bypass_egress_vlan_filter1;
+       /** Enable egress VLAN filter 1 */
+       bool egress_vlan_filter1enable;
+       /** VLAN filter block 1 of egress traffic if
+           \ref MXL862XX_BRIDGE_port_config_t::b_egress_vlan_filter1Enable is TRUE. */
+       u16 egress_vlan_filter1block_id;
+       /** VLAN filter block 1 size. This is optional.
+           If it is 0, the block size of n_egress_vlan_filter1Block_id will be used.
+           Otherwise, this field will be used. */
+       u16 egress_vlan_filter1block_size;
+       /** Enable egress VLAN filter 2 */
+       bool egress_vlan_filter2enable;
+       /** VLAN filter block 2 of egress traffic if
+           \ref MXL862XX_BRIDGE_port_config_t::b_egress_vlan_filter2Enable is TRUE. */
+       u16 egress_vlan_filter2block_id;
+       /** VLAN filter block 2 size. This is optional.
+           If it is 0, the block size of n_egress_vlan_filter2Block_id will be used.
+           Otherwise, this field will be used. */
+       u16 egress_vlan_filter2block_size;
+#ifdef SCK_FW_1_0_41
+       /** Enable Ingress VLAN Based Mac Learning */
+       bool ingress_vlan_based_mac_learning_enable;
+       /** 0 - Intermediate outer VLAN
+           tag is used for MAC address/multicast
+           learning, lookup and filtering.
+           1 - Original outer VLAN tag is used
+           for MAC address/multicast learning, lookup
+           and filtering. */
+#endif
+       bool vlan_tag_selection;
+       /** 0 - Disable, VLAN Priority field is not used
+           and value 0 is used for source MAC address
+           learning and filtering.
+           1 - Enable, VLAN Priority field is used for
+           source MAC address learning and filtering. */
+       bool vlan_src_mac_priority_enable;
+       /** 0 - Disable, VLAN DEI/CFI field is not used
+           and value 0 is used for source MAC address
+           learning and filtering.
+           1 -  Enable, VLAN DEI/CFI field is used for
+           source MAC address learning and filtering */
+       bool vlan_src_mac_dei_enable;
+       /** 0 - Disable, VLAN ID field is not used and
+           value 0 is used for source MAC address
+           learning and filtering
+           1 - Enable, VLAN ID field is used for source
+           MAC address learning and filtering. */
+       bool vlan_src_mac_vid_enable;
+       /** 0 - Disable, VLAN Priority field is not used
+           and value 0 is used for destination MAC
+           address look up and filtering.
+           1 - Enable, VLAN Priority field is used for
+           destination MAC address look up and
+           filtering */
+       bool vlan_dst_mac_priority_enable;
+       /** 0 - Disable, VLAN CFI/DEI field is not used
+           and value 0 is used for destination MAC
+           address lookup and filtering.
+           1 - Enable, VLAN CFI/DEI field is used for
+           destination MAC address look up and
+           filtering. */
+       bool vlan_dst_mac_dei_enable;
+       /** 0 - Disable, VLAN ID field is not used and
+           value 0 is used for destination MAC address
+           look up and filtering.
+           1 - Enable, VLAN ID field is destination for
+           destination MAC address look up and
+           filtering. */
+       bool vlan_dst_mac_vid_enable;
+#ifdef SCK_FW_1_0_41
+       /** Enable, VLAN Based Multicast Lookup */
+       bool vlan_based_multi_cast_lookup;
+       /** 0 - Disable, VLAN Priority field is not used
+           and value 0 is used for IP multicast lookup.
+           1 - Enable, VLAN Priority field is used for IP
+           multicast lookup. */
+#endif
+       bool vlan_multicast_priority_enable;
+       /** 0 - Disable, VLAN CFI/DEI field is not used
+           and value 0 is used for IP multicast lookup.
+           1 - Enable, VLAN CFI/DEI field is used for IP
+           multicast lookup. */
+       bool vlan_multicast_dei_enable;
+       /** 0 - Disable, VLAN ID field is not used and
+           value 0 is used for IP multicast lookup.
+           1 - Enable, VLAN ID field is destination for IP
+           multicast lookup. */
+       bool vlan_multicast_vid_enable;
+} mxl862xx_bridge_port_config_t;
+
+/** \brief Bridge Allocation.
+    Used by \ref MXL862XX_BRIDGE_ALLOC and \ref MXL862XX_BRIDGE_FREE. */
+typedef struct {
+       /** If \ref MXL862XX_BRIDGE_ALLOC is successful, a valid ID will be returned
+           in this field. Otherwise, \ref INVALID_HANDLE is returned in this field.
+           For \ref MXL862XX_BRIDGE_FREE, this field should be valid ID returned by
+           \ref MXL862XX_BRIDGE_ALLOC. ID 0 is special Bridge created during
+           initialization. */
+       u16 bridge_id;
+} mxl862xx_bridge_alloc_t;
+
+/** \brief Ethernet port speed mode.
+    For certain generations of GSWIP, a port might support only a subset of the possible settings.
+    Used by \ref MXL862XX_port_link_cfg_t. */
+typedef enum {
+       /** 10 Mbit/s */
+       MXL862XX_PORT_SPEED_10,
+       /** 100 Mbit/s */
+       MXL862XX_PORT_SPEED_100,
+       /** 200 Mbit/s */
+       MXL862XX_PORT_SPEED_200,
+       /** 1000 Mbit/s */
+       MXL862XX_PORT_SPEED_1000,
+       /** 2.5 Gbit/s */
+       MXL862XX_PORT_SPEED_2500,
+       /** 5 Gbit/s */
+       MXL862XX_PORT_SPEED_5000,
+       /** 10 Gbit/s */
+       MXL862XX_PORT_SPEED_10000,
+       /** Auto speed for XGMAC */
+       MXL862XX_PORT_SPEED_AUTO,
+} mxl862xx_port_speed_t;
+
+/** \brief Ethernet port duplex status.
+    Used by \ref MXL862XX_port_link_cfg_t. */
+typedef enum {
+       /** Port operates in full-duplex mode */
+       MXL862XX_DUPLEX_FULL = 0,
+       /** Port operates in half-duplex mode */
+       MXL862XX_DUPLEX_HALF = 1,
+       /** Port operates in Auto mode */
+       MXL862XX_DUPLEX_AUTO = 2,
+} mxl862xx_port_duplex_t;
+
+/** \brief Force the MAC and PHY link modus.
+    Used by \ref MXL862XX_port_link_cfg_t. */
+typedef enum {
+       /** Link up. Any connected LED
+           still behaves based on the real PHY status. */
+       MXL862XX_PORT_LINK_UP = 0,
+       /** Link down. */
+       MXL862XX_PORT_LINK_DOWN = 1,
+       /** Link Auto. */
+       MXL862XX_PORT_LINK_AUTO = 2,
+} mxl862xx_port_link_t;
+
+/** \brief Ethernet port interface mode.
+    A port might support only a subset of the possible settings.
+    Used by \ref MXL862XX_port_link_cfg_t. */
+typedef enum {
+       /** Normal PHY interface (twisted pair), use the internal MII Interface. */
+       MXL862XX_PORT_HW_MII = 0,
+       /** Reduced MII interface in normal mode. */
+       MXL862XX_PORT_HW_RMII = 1,
+       /** GMII or MII, depending upon the speed. */
+       MXL862XX_PORT_HW_GMII = 2,
+       /** RGMII mode. */
+       MXL862XX_PORT_HW_RGMII = 3,
+       /** XGMII mode. */
+       MXL862XX_PORT_HW_XGMII = 4,
+} mxl862xx_mii_mode_t;
+
+/** \brief Ethernet port configuration for PHY or MAC mode.
+    Used by \ref MXL862XX_port_link_cfg_t. */
+typedef enum {
+       /** MAC Mode. The Ethernet port is configured to work in MAC mode. */
+       MXL862XX_PORT_MAC = 0,
+       /** PHY Mode. The Ethernet port is configured to work in PHY mode. */
+       MXL862XX_PORT_PHY = 1
+} mxl862xx_mii_type_t;
+
+/** \brief Ethernet port clock source configuration.
+    Used by \ref MXL862XX_port_link_cfg_t. */
+typedef enum {
+       /** Clock Mode not applicable. */
+       MXL862XX_PORT_CLK_NA = 0,
+       /** Clock Master Mode. The port is configured to provide the clock as output signal. */
+       MXL862XX_PORT_CLK_MASTER = 1,
+       /** Clock Slave Mode. The port is configured to use the input clock signal. */
+       MXL862XX_PORT_CLK_SLAVE = 2
+} mxl862xx_clk_mode_t;
+
+/** \brief Ethernet port link, speed status and flow control status.
+    Used by \ref MXL862XX_PORT_LINK_CFG_GET and \ref MXL862XX_PORT_LINK_CFG_SET. */
+typedef struct {
+       /** Ethernet Port number (zero-based counting). The valid range is hardware
+           dependent. An error code is delivered if the selected port is not
+           available. */
+       u16 port_id;
+       /** Force Port Duplex Mode.
+
+           - 0: Negotiate Duplex Mode. Auto-negotiation mode. Negotiated
+             duplex mode given in 'e_duplex'
+             during MXL862XX_PORT_LINK_CFG_GET calls.
+           - 1: Force Duplex Mode. Force duplex mode in 'e_duplex'.
+       */
+       bool duplex_force;
+       /** Port Duplex Status. */
+       mxl862xx_port_duplex_t duplex;
+       /** Force Link Speed.
+
+           - 0: Negotiate Link Speed. Negotiated speed given in
+             'e_speed' during MXL862XX_PORT_LINK_CFG_GET calls.
+           - 1: Force Link Speed. Forced speed mode in 'e_speed'.
+       */
+       bool speed_force;
+       /** Ethernet port link up/down and speed status. */
+       mxl862xx_port_speed_t speed;
+       /** Force Link.
+
+            - 0: Auto-negotiate Link. Current link status is given in
+              'e_link' during MXL862XX_PORT_LINK_CFG_GET calls.
+            - 1: Force Duplex Mode. Force duplex mode in 'e_link'.
+        */
+       bool link_force;
+       /** Link Status. Read out the current link status.
+           Note that the link could be forced by setting 'b_link_force'. */
+       mxl862xx_port_link_t link;
+       /** Selected interface mode (MII/RMII/RGMII/GMII/XGMII). */
+       mxl862xx_mii_mode_t mii_mode;
+       /** Select MAC or PHY mode (PHY = Reverse x_mII). */
+       mxl862xx_mii_type_t mii_type;
+       /** Interface Clock mode (used for RMII mode). */
+       mxl862xx_clk_mode_t clk_mode;
+       /** 'Low Power Idle' Support for 'Energy Efficient Ethernet'.
+           Only enable this feature in case the attached PHY also supports it. */
+       bool lpi;
+} mxl862xx_port_link_cfg_t;
+
+/** \brief Port Enable Type Selection.
+    Used by \ref MXL862XX_port_cfg_t. */
+typedef enum {
+       /** The port is disabled in both directions. */
+       MXL862XX_PORT_DISABLE = 0,
+       /** The port is enabled in both directions (ingress and egress). */
+       MXL862XX_PORT_ENABLE_RXTX = 1,
+       /** The port is enabled in the receive (ingress) direction only. */
+       MXL862XX_PORT_ENABLE_RX = 2,
+       /** The port is enabled in the transmit (egress) direction only. */
+       MXL862XX_PORT_ENABLE_TX = 3
+} mxl862xx_port_enable_t;
+
+/** \brief Ethernet flow control status.
+    Used by \ref MXL862XX_port_cfg_t. */
+typedef enum {
+       /** Automatic flow control mode selection through auto-negotiation. */
+       MXL862XX_FLOW_AUTO = 0,
+       /** Receive flow control only */
+       MXL862XX_FLOW_RX = 1,
+       /** Transmit flow control only */
+       MXL862XX_FLOW_TX = 2,
+       /** Receive and Transmit flow control */
+       MXL862XX_FLOW_RXTX = 3,
+       /** No flow control */
+       MXL862XX_FLOW_OFF = 4
+} mxl862xx_port_flow_t;
+
+/** \brief Port Mirror Options.
+    Used by \ref MXL862XX_port_cfg_t. */
+typedef enum {
+       /** Mirror Feature is disabled. Normal port usage. */
+       MXL862XX_PORT_MONITOR_NONE = 0,
+       /** Port Ingress packets are mirrored to the monitor port. */
+       MXL862XX_PORT_MONITOR_RX = 1,
+       /** Port Egress packets are mirrored to the monitor port. */
+       MXL862XX_PORT_MONITOR_TX = 2,
+       /** Port Ingress and Egress packets are mirrored to the monitor port. */
+       MXL862XX_PORT_MONITOR_RXTX = 3,
+       /** Packet mirroring of 'unknown VLAN violation' frames. */
+       MXL862XX_PORT_MONITOR_VLAN_UNKNOWN = 4,
+       /** Packet mirroring of 'VLAN ingress or egress membership violation' frames. */
+       MXL862XX_PORT_MONITOR_VLAN_MEMBERSHIP = 16,
+       /** Packet mirroring of 'port state violation' frames. */
+       MXL862XX_PORT_MONITOR_PORT_STATE = 32,
+       /** Packet mirroring of 'MAC learning limit violation' frames. */
+       MXL862XX_PORT_MONITOR_LEARNING_LIMIT = 64,
+       /** Packet mirroring of 'port lock violation' frames. */
+       MXL862XX_PORT_MONITOR_PORT_LOCK = 128
+} mxl862xx_port_monitor_t;
+
+/** \brief Interface RMON Counter Mode - (FID, SUBID or FLOWID) Config - GSWIP-3.0 only.
+    Used by \ref MXL862XX_port_cfg_t. */
+typedef enum {
+       /** FID based Interface RMON counters Usage */
+       MXL862XX_IF_RMON_FID = 0,
+       /** Sub-Interface Id based Interface RMON counters Usage */
+       MXL862XX_IF_RMON_SUBID = 1,
+       /** Flow Id (LSB bits 3 to 0) based Interface RMON counters Usage */
+       MXL862XX_IF_RMON_FLOWID_LSB = 2,
+       /** Flow Id (MSB bits 7 to 4) based Interface RMON counters Usage */
+       MXL862XX_IF_RMON_FLOWID_MSB = 3
+} mxl862xx_if_rmon_mode_t;
+
+/** \brief Port Configuration Parameters.
+    Used by \ref MXL862XX_PORT_CFG_GET and \ref MXL862XX_PORT_CFG_SET. */
+typedef struct {
+       /** Port Type. This gives information which type of port is configured.
+           n_port_id should be based on this field. */
+       mxl862xx_port_type_t port_type;
+
+       /** Ethernet Port number (zero-based counting). The valid range is hardware
+           dependent. An error code is delivered if the selected port is not
+           available. */
+       u16 port_id;
+       /** Enable Port (ingress only, egress only, both directions, or disabled).
+           This parameter is used for Spanning Tree Protocol and 802.1X applications. */
+       mxl862xx_port_enable_t enable;
+       /** Drop unknown unicast packets.
+           Do not send out unknown unicast packets on this port,
+           if the boolean parameter is enabled. By default packets of this type
+           are forwarded to this port. */
+       bool unicast_unknown_drop;
+       /** Drop unknown multicast packets.
+           Do not send out unknown multicast packets on this port,
+           if boolean parameter is enabled. By default packets of this type
+           are forwarded to this port.
+           Some platforms also drop broadcast packets. */
+       bool multicast_unknown_drop;
+       /** Drop reserved packet types
+           (destination address from '01 80 C2 00 00 00' to
+           '01 80 C2 00 00 2F') received on this port. */
+       bool reserved_packet_drop;
+       /** Drop Broadcast packets received on this port. By default packets of this
+         type are forwarded to this port. */
+       bool broadcast_drop;
+       /** Enables MAC address table aging.
+           The MAC table entries learned on this port are removed after the
+           aging time has expired.
+           The aging time is a global parameter, common to all ports. */
+       bool aging;
+       /** MAC address table learning on the port specified by 'n_port_id'.
+           By default this parameter is always enabled. */
+       bool learning;
+       /** Automatic MAC address table learning locking on the port specified
+           by 'n_port_id'.
+           This parameter is only taken into account when 'b_learning' is enabled. */
+       bool learning_mac_port_lock;
+       /** Automatic MAC address table learning limitation on this port.
+           The learning functionality is disabled when the limit value is zero.
+           The value 0x_fFFF to allow unlimited learned address.
+           This parameter is only taken into account when 'b_learning' is enabled. */
+       u16 learning_limit;
+       /** MAC spoofing detection. Identifies ingress packets that carry
+           a MAC source address which was previously learned on a different ingress
+           port (learned by MAC bridging table). This also applies to static added
+           entries. Those violated packets could be accepted or discarded,
+           depending on the global switch configuration 'b_mAC_Spoofing_action'.
+           This parameter is only taken into account when 'b_learning' is enabled. */
+       bool mac_spoofing_detection;
+       /** Port Flow Control Status. Enables the flow control function. */
+       mxl862xx_port_flow_t flow_ctrl;
+       /** Port monitor feature. Allows forwarding of egress and/or ingress
+           packets to the monitor port. If enabled, the monitor port gets
+           a copy of the selected packet type. */
+       mxl862xx_port_monitor_t port_monitor;
+       /** Assign Interface RMON Counters for this Port - GSWIP-3.0 */
+       bool if_counters;
+       /** Interface RMON Counters Start Index - GSWIP-3.0.
+           Value of (-1) denotes unassigned Interface Counters.
+           Valid range : 0-255 available to be shared amongst ports in desired way*/
+       int if_count_start_idx;
+       /** Interface RMON Counters Mode - GSWIP-3.0 */
+       mxl862xx_if_rmon_mode_t if_rmonmode;
+} mxl862xx_port_cfg_t;
+
+#define MXL862XX_ERROR_BASE 1024
+/** \brief Enumeration for function status return. The upper four bits are reserved for error classification */
+typedef enum {
+       /** Correct or Expected Status */
+       MXL862XX_STATUS_OK = 0,
+       /** Generic or unknown error occurred */
+       MXL862XX_STATUS_ERR = -1,
+       /** Invalid function parameter */
+       MXL862XX_STATUS_PARAM = -(MXL862XX_ERROR_BASE + 2),
+       /** No space left in VLAN table */
+       MXL862XX_STATUS_VLAN_SPACE = -(MXL862XX_ERROR_BASE + 3),
+       /** Requested VLAN ID not found in table */
+       MXL862XX_STATUS_VLAN_ID = -(MXL862XX_ERROR_BASE + 4),
+       /** Invalid ioctl */
+       MXL862XX_STATUS_INVAL_IOCTL = -(MXL862XX_ERROR_BASE + 5),
+       /** Operation not supported by hardware */
+       MXL862XX_STATUS_NO_SUPPORT = -(MXL862XX_ERROR_BASE + 6),
+       /** Timeout */
+       MXL862XX_STATUS_TIMEOUT = -(MXL862XX_ERROR_BASE + 7),
+       /** At least one value is out of range */
+       MXL862XX_STATUS_VALUE_RANGE = -(MXL862XX_ERROR_BASE + 8),
+       /** The port_id/queue_id/meter_id/etc. is not available in this hardware or the
+           selected feature is not available on this port */
+       MXL862XX_STATUS_PORT_INVALID = -(MXL862XX_ERROR_BASE + 9),
+       /** The interrupt is not available in this hardware */
+       MXL862XX_STATUS_IRQ_INVALID = -(MXL862XX_ERROR_BASE + 10),
+       /** The MAC table is full, an entry could not be added */
+       MXL862XX_STATUS_MAC_TABLE_FULL = -(MXL862XX_ERROR_BASE + 11),
+       /** Locking failed - SWAPI is busy */
+       MXL862XX_STATUS_LOCK_FAILED = -(MXL862XX_ERROR_BASE + 12),
+       /** Multicast Forwarding table entry not found */
+       MXL862XX_STATUS_ENTRY_NOT_FOUND = -(MXL862XX_ERROR_BASE + 13),
+} mxl862xx_return_t;
+
+/** \brief MAC Table Clear Type
+    Used by \ref MXL862XX_MAC_table_clear_cond_t */
+typedef enum {
+       /** Clear dynamic entries based on Physical Port */
+       MXL862XX_MAC_CLEAR_PHY_PORT = 0,
+       /** Clear all dynamic entries */
+       MXL862XX_MAC_CLEAR_DYNAMIC = 1,
+} mxl862xx_mac_clear_type_t;
+
+/** \brief MAC Table Clear based on given condition.
+    Used by \ref MXL862XX_MAC_TABLE_CLEAR_COND. */
+typedef struct {
+       /** MAC table clear type \ref MXL862XX_Mac_clear_type_t */
+       u8 type;
+       union {
+               /** Physical port id (0~16) if \ref e_type is
+                   ref MXL862XX_MAC_CLEAR_PHY_PORT. */
+               u8 port_id;
+       };
+} mxl862xx_mac_table_clear_cond_t;
+
+/** \brief Configures the Spanning Tree Protocol state of an Ethernet port.
+    Used by \ref MXL862XX_STP_PORT_CFG_SET
+    and \ref MXL862XX_STP_PORT_CFG_GET. */
+typedef struct {
+       /** Ethernet Port number (zero-based counting) in GSWIP-2.1/2.2/3.0. From
+           GSWIP-3.1, this field is Bridge Port ID. The valid range is hardware
+           dependent. An error code is delivered if the selected port is not
+           available. */
+       u16 port_id;
+       /** Filtering Identifier (FID) (not supported by all switches).
+           The FID allows to keep multiple STP states per physical Ethernet port.
+           Multiple CTAG VLAN groups could be a assigned to one FID and therefore
+           share the same STP port state. Switch API ignores the FID value
+           in case the switch device does not support it or switch CTAG VLAN
+           awareness is disabled. */
+       u16 fid;
+       /** Spanning Tree Protocol state of the port. */
+       mxl862xx_stp_port_state_t port_state;
+} mxl862xx_stp_port_cfg_t;
+
+/** \brief Special tag Ethertype mode */
+typedef enum {
+       /** The ether_type field of the Special Tag of egress packets is always set
+           to a prefined value. This same defined value applies for all
+           switch ports. */
+       MXL862XX_CPU_ETHTYPE_PREDEFINED = 0,
+       /** The Ethertype field of the Special Tag of egress packets is set to
+           the flow_iD parameter, which is a results of the switch flow
+           classification result. The switch flow table rule provides this
+           flow_iD as action parameter. */
+       MXL862XX_CPU_ETHTYPE_FLOWID = 1
+} mxl862xx_cpu_special_tag_eth_type_t;
+
+/** \brief Parser Flags and Offsets Header settings on CPU Port for GSWIP-3.0.
+    Used by \ref MXL862XX_CPU_Port_cfg_t. */
+typedef enum {
+       /** No Parsing Flags or Offsets accompanying to CPU Port for this combination */
+       MXL862XX_CPU_PARSER_NIL = 0,
+       /** 8-Bytes Parsing Flags (Bit 63:0) accompanying to CPU Port for this combination */
+       MXL862XX_CPU_PARSER_FLAGS = 1,
+       /** 40-Bytes Offsets (Offset-0 to -39) + 8-Bytes Parsing Flags (Bit 63:0) accompanying to CPU Port for this combination */
+       MXL862XX_CPU_PARSER_OFFSETS_FLAGS = 2,
+       /** Reserved - for future use */
+       MXL862XX_CPU_PARSER_RESERVED = 3
+} mxl862xx_cpu_parser_header_cfg_t;
+
+/** \brief FCS and Pad Insertion operations for GSWIP 3.1
+    Used by \ref MXL862XX_CPU_Port_cfg_set/Get. */
+typedef enum {
+       /** CRC Pad Insertion Enable */
+       MXL862XX_CRC_PAD_INS_EN = 0,
+       /** CRC Insertion Enable Pad Insertion Disable */
+       MXL862XX_CRC_EN_PAD_DIS = 1,
+       /** CRC Pad Insertion Disable */
+       MXL862XX_CRC_PAD_INS_DIS = 2
+} mxl862xx_fcs_tx_ops_t;
+
+/** \brief Defines one port that is directly connected to the CPU and its applicable settings.
+    Used by \ref MXL862XX_CPU_PORT_CFG_SET and \ref MXL862XX_CPU_PORT_CFG_GET. */
+typedef struct {
+       /** Ethernet Port number (zero-based counting) set to CPU Port. The valid number is hardware
+           dependent. (E.g. Port number 0 for GSWIP-3.0 or 6 for GSWIP-2.x). An error code is delivered if the selected port is not
+           available. */
+       u16 port_id;
+       /** CPU port validity.
+           Set command: set true to define a CPU port, set false to undo the setting.
+           Get command: true if defined as CPU, false if not defined as CPU port. */
+       bool cPU_Port_valid;
+       /** Special tag enable in ingress direction. */
+       bool special_tag_ingress;
+       /** Special tag enable in egress direction. */
+       bool special_tag_egress;
+       /** Enable FCS check
+
+           - false: No check, forward all frames
+           - 1: Check FCS, drop frames with errors
+       */
+       bool fcs_check;
+       /** Enable FCS generation
+
+           - false: Forward packets without FCS
+           - 1: Generate FCS for all frames
+       */
+       bool fcs_generate;
+       /** Special tag Ethertype mode. Not applicable to GSWIP-3.1. */
+       mxl862xx_cpu_special_tag_eth_type_t special_tag_eth_type;
+       /** GSWIP-3.0 specific Parser Header Config for no MPE flags (i.e. MPE1=0, MPE2=0). */
+       mxl862xx_cpu_parser_header_cfg_t no_mpeparser_cfg;
+       /** GSWIP-3.0 specific Parser Header Config for MPE-1 set flag (i.e. MPE1=1, MPE2=0). */
+       mxl862xx_cpu_parser_header_cfg_t mpe1parser_cfg;
+       /** GSWIP-3.0 specific Parser Header Config for MPE-2 set flag (i.e. MPE1=0, MPE2=1). */
+       mxl862xx_cpu_parser_header_cfg_t mpe2parser_cfg;
+       /** GSWIP-3.0 specific Parser Header Config for both MPE-1 and MPE-2 set flag (i.e. MPE1=1, MPE2=1). */
+       mxl862xx_cpu_parser_header_cfg_t mpe1mpe2parser_cfg;
+       /** GSWIP-3.1 FCS tx Operations. */
+       mxl862xx_fcs_tx_ops_t fcs_tx_ops;
+       /** GSWIP-3.2 Time Stamp Field Removal for PTP Packet
+           0 - DIS Removal is disabled
+           1 - EN Removal is enabled
+       */
+       bool ts_ptp;
+       /** GSWIP-3.2 Time Stamp Field Removal for Non-PTP Packet
+           0 - DIS Removal is disabled
+           1 - EN Removal is enabled
+       */
+       bool ts_nonptp;
+} mxl862xx_cpu_port_cfg_t;
+
+typedef struct {
+       /* port ID (1~16) */
+       u8 pid;
+
+       /* bit value 1 to indicate valid field
+        *   bit 0 - rx
+        *       1 - tx
+        *       2 - rx_pen
+        *       3 - tx_pen
+        */
+       u8 mask;
+
+       /* RX special tag mode
+        *  value 0 - packet does NOT have special tag and special tag is
+        *            NOT inserted
+        *        1 - packet does NOT have special tag and special tag is
+        *            inserted
+        *        2 - packet has special tag and special tag is NOT inserted
+        */
+       u8 rx;
+
+       /* TX special tag mode
+        *  value 0 - packet does NOT have special tag and special tag is
+        *            NOT removed
+        *        1 - packet has special tag and special tag is replaced
+        *        2 - packet has special tag and special tag is NOT removed
+        *        3 - packet has special tag and special tag is removed
+        */
+       u8 tx;
+
+       /* RX special tag info over preamble
+        *  value 0 - special tag info inserted from byte 2 to 7 are all 0
+        *        1 - special tag byte 5 is 16, other bytes from 2 to 7 are 0
+        *        2 - special tag byte 5 is from preamble field, others are 0
+        *        3 - special tag byte 2 to 7 are from preabmle field
+        */
+       u8 rx_pen;
+
+       /* TX special tag info over preamble
+        *  value 0 - disabled
+        *        1 - enabled
+        */
+       u8 tx_pen;
+} mxl862xx_ss_sp_tag_t;
+
+/** \brief Port monitor configuration.
+    Used by \ref MXL862XX_MONITOR_PORT_CFG_GET and \ref MXL862XX_MONITOR_PORT_CFG_SET. */
+typedef struct {
+       /** Ethernet Port number (zero-based counting). The valid range is hardware
+           dependent. An error code is delivered if the selected port is not
+           available. */
+       u8 port_id;
+       /* Monitoring Sub-IF id */
+       u16 sub_if_id;
+       /* Out of use. */
+       bool monitor_port;
+} mxl862xx_monitor_port_cfg_t;
+
+/** \brief VLAN Filter TCI Mask.
+    Used by \ref MXL862XX_VLANFILTER_config_t */
+typedef enum {
+       MXL862XX_VLAN_FILTER_TCI_MASK_VID = 0,
+       MXL862XX_VLAN_FILTER_TCI_MASK_PCP = 1,
+       MXL862XX_VLAN_FILTER_TCI_MASK_TCI = 2
+} mxl862xx_vlan_filter_tci_mask_t;
+
+typedef enum {
+       MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_1 = 0,
+       MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_2 = 1,
+       MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_3 = 2,
+       MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_4 = 3
+} mxl862xx_extended_vlan_4_tpid_mode_t;
+
+/** \brief Extended VLAN Filter TPID Field.
+    Used by \ref MXL862XX_EXTENDEDVLAN_filter_vLAN_t. */
+typedef enum {
+       /** Do not filter. */
+       MXL862XX_EXTENDEDVLAN_FILTER_TPID_NO_FILTER = 0,
+       /** TPID is 0x8100. */
+       MXL862XX_EXTENDEDVLAN_FILTER_TPID_8021Q = 1,
+       /** TPID is global configured value. */
+       MXL862XX_EXTENDEDVLAN_FILTER_TPID_VTETYPE = 2
+} mxl862xx_extended_vlan_filter_tpid_t;
+
+/** \brief Extended VLAN Treatment Set TPID.
+   Used by \ref MXL862XX_EXTENDEDVLAN_treatment_vlan_t. */
+typedef enum {
+       /** TPID is copied from inner VLAN tag of received packet. */
+       MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_TPID = 0,
+       /** TPID is copied from outer VLAN tag of received packet. */
+       MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_TPID = 1,
+       /** TPID is global configured value. */
+       MXL862XX_EXTENDEDVLAN_TREATMENT_VTETYPE = 2,
+       /** TPID is 0x8100. */
+       MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q = 3
+} mxl862xx_extended_vlan_treatment_tpid_t;
+
+/** \brief Extended VLAN Filter DEI Field.
+    Used by \ref MXL862XX_EXTENDEDVLAN_filter_vLAN_t. */
+typedef enum {
+       /** Do not filter. */
+       MXL862XX_EXTENDEDVLAN_FILTER_DEI_NO_FILTER = 0,
+       /** DEI is 0. */
+       MXL862XX_EXTENDEDVLAN_FILTER_DEI_0 = 1,
+       /** DEI is 1. */
+       MXL862XX_EXTENDEDVLAN_FILTER_DEI_1 = 2
+} mxl862xx_extended_vlan_filter_dei_t;
+
+/** \brief Extended VLAN Treatment Set DEI.
+   Used by \ref MXL862XX_EXTENDEDVLAN_treatment_vlan_t. */
+typedef enum {
+       /** DEI (if applicable) is copied from inner VLAN tag of received packet. */
+       MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_DEI = 0,
+       /** DEI (if applicable) is copied from outer VLAN tag of received packet. */
+       MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_DEI = 1,
+       /** DEI is 0. */
+       MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0 = 2,
+       /** DEI is 1. */
+       MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_1 = 3
+} mxl862xx_extended_vlan_treatment_dei_t;
+
+/** \brief Extended VLAN Filter Type.
+    Used by \ref MXL862XX_EXTENDEDVLAN_filter_vLAN_t. */
+typedef enum {
+       /** There is tag and criteria applies. */
+       MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL = 0,
+       /** There is tag but no criteria. */
+       MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER = 1,
+       /** Default entry if no other rule applies. */
+       MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT = 2,
+       /** There is no tag. */
+       MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG = 3,
+       /** Block invalid*/
+       MXL862XX_EXTENDEDVLAN_BLOCK_INVALID = 4
+} mxl862xx_extended_vlan_filter_type_t;
+
+/** \brief Extended VLAN Filter ether_type.
+    Used by \ref MXL862XX_EXTENDEDVLAN_filter_vLAN_t. */
+typedef enum {
+       /** Do not filter. */
+       MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_NO_FILTER = 0,
+       /** IPo_e frame (Ethertyp is 0x0800). */
+       MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_IPOE = 1,
+       /** PPPo_e frame (Ethertyp is 0x8863 or 0x8864). */
+       MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_PPPOE = 2,
+       /** ARP frame (Ethertyp is 0x0806). */
+       MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_ARP = 3,
+       /** IPv6 IPo_e frame (Ethertyp is 0x86DD). */
+       MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_IPV6IPOE = 4,
+       /** EAPOL (Ethertyp is 0x888E). */
+       MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_EAPOL = 5,
+       /** DHCPV4 (UDP DESTINATION PORT 67&68). */
+       MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_DHCPV4 = 6,
+       /** DHCPV6 (UDP DESTINATION PORT 546&547). */
+       MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_DHCPV6 = 7
+} mxl862xx_extended_vlan_filter_ethertype_t;
+
+/** \brief Extended VLAN Treatment Set Priority.
+   Used by \ref MXL862XX_EXTENDEDVLAN_treatment_vlan_t. */
+typedef enum {
+       /** Set priority with given value. */
+       MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL = 0,
+       /** Prority value is copied from inner VLAN tag of received packet. */
+       MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_PRORITY = 1,
+       /** Prority value is copied from outer VLAN tag of received packet. */
+       MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_PRORITY = 2,
+       /** Prority value is derived from DSCP field of received packet. */
+       MXL862XX_EXTENDEDVLAN_TREATMENT_DSCP = 3
+} mxl862xx_extended_vlan_treatment_priority_t;
+
+/** \brief Extended VLAN Treatment Set VID.
+   Used by \ref MXL862XX_EXTENDEDVLAN_treatment_vlan_t. */
+typedef enum {
+       /** Set VID with given value. */
+       MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL = 0,
+       /** VID is copied from inner VLAN tag of received packet. */
+       MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_VID = 1,
+       /** VID is copied from outer VLAN tag of received packet. */
+       MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_VID = 2,
+} mxl862xx_extended_vlan_treatment_vid_t;
+
+/** \brief Extended VLAN Treatment Remove Tag.
+    Used by \ref MXL862XX_EXTENDEDVLAN_treatment_vlan_t. */
+typedef enum {
+       /** Do not remove VLAN tag. */
+       MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG = 0,
+       /** Remove 1 VLAN tag following DA/SA. */
+       MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG = 1,
+       /** Remove 2 VLAN tag following DA/SA. */
+       MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_2_TAG = 2,
+       /** Discard upstream traffic. */
+       MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM = 3,
+} mxl862xx_extended_vlan_treatment_remove_tag_t;
+
+/** \brief Extended VLAN Filter VLAN Tag.
+    Used by \ref MXL862XX_EXTENDEDVLAN_filter_t. */
+typedef struct {
+       /** Filter Type: normal filter, default rule, or no tag */
+       mxl862xx_extended_vlan_filter_type_t type;
+       /** Enable priority field filtering. */
+       bool priority_enable;
+       /** Filter priority value if b_priority_enable is TRUE. */
+       u32 priority_val;
+       /** Enable VID filtering. */
+       bool vid_enable;
+       /** Filter VID if b_vid_enable is TRUE. */
+       u32 vid_val;
+       /** Mode to filter TPID of VLAN tag. */
+       mxl862xx_extended_vlan_filter_tpid_t tpid;
+       /** Mode to filter DEI of VLAN tag. */
+       mxl862xx_extended_vlan_filter_dei_t dei;
+} mxl862xx_extendedvlan_filter_vlan_t;
+
+/** \brief Extended VLAN Treatment VLAN Tag.
+    Used by \ref MXL862XX_EXTENDEDVLAN_treatment_t. */
+typedef struct {
+       /** Select source of priority field of VLAN tag. */
+       mxl862xx_extended_vlan_treatment_priority_t priority_mode;
+       /** If \ref MXL862XX_EXTENDEDVLAN_treatment_vlan_t::priority_mode is
+           \ref MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL, use this value for
+           priority field of VLAN tag. */
+       u32 priority_val;
+       /** Select source of VID field of VLAN tag. */
+       mxl862xx_extended_vlan_treatment_vid_t vid_mode;
+       /** If \ref MXL862XX_EXTENDEDVLAN_treatment_vlan_t::e_vid_mode is
+           \ref MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL, use this value for VID field
+           of VLAN tag. */
+       u32 vid_val;
+       /** Select source of TPID field of VLAN tag. */
+       mxl862xx_extended_vlan_treatment_tpid_t tpid;
+       /** Select source of DEI field of VLAN tag. */
+       mxl862xx_extended_vlan_treatment_dei_t dei;
+} mxl862xx_extendedvlan_treatment_vlan_t;
+
+/** \brief Extended VLAN Filter.
+    Used by \ref MXL862XX_EXTENDEDVLAN_config_t. */
+typedef struct {
+       /** Filter on Original Packet. */
+       bool original_packet_filter_mode;
+       mxl862xx_extended_vlan_4_tpid_mode_t filter_4_tpid_mode;
+       /** Filter for outer VLAN tag. */
+       mxl862xx_extendedvlan_filter_vlan_t outer_vlan;
+       /** Filter for inner VLAN tag. */
+       mxl862xx_extendedvlan_filter_vlan_t inner_vlan;
+       /** Filter ether_type. */
+       mxl862xx_extended_vlan_filter_ethertype_t ether_type;
+} mxl862xx_extendedvlan_filter_t;
+
+/** \brief Extended VLAN Allocation.
+    Used by \ref MXL862XX_EXTENDEDVLAN_ALLOC and \ref MXL862XX_EXTENDEDVLAN_FREE. */
+typedef struct {
+       /** Total number of extended VLAN entries are requested. Proper value should
+           be given for \ref MXL862XX_EXTENDEDVLAN_ALLOC. This field is ignored for
+           \ref MXL862XX_EXTENDEDVLAN_FREE. */
+       u16 number_of_entries;
+
+       /** If \ref MXL862XX_EXTENDEDVLAN_ALLOC is successful, a valid ID will be returned
+           in this field. Otherwise, \ref INVALID_HANDLE is returned in this field.
+           For \ref MXL862XX_EXTENDEDVLAN_FREE, this field should be valid ID returned by
+           \ref MXL862XX_EXTENDEDVLAN_ALLOC. */
+       u16 extended_vlan_block_id;
+} mxl862xx_extendedvlan_alloc_t;
+
+/** \brief Extended VLAN Treatment.
+    Used by \ref MXL862XX_EXTENDEDVLAN_config_t. */
+typedef struct {
+       /** Number of VLAN tag(s) to remove. */
+       mxl862xx_extended_vlan_treatment_remove_tag_t remove_tag;
+
+       mxl862xx_extended_vlan_4_tpid_mode_t treatment_4_tpid_mode;
+
+       /** Enable outer VLAN tag add/modification. */
+       bool add_outer_vlan;
+       /** If b_add_outer_vlan is TRUE, add or modify outer VLAN tag. */
+       mxl862xx_extendedvlan_treatment_vlan_t outer_vlan;
+
+       /** Enable inner VLAN tag add/modification. */
+       bool add_inner_vlan;
+       /** If b_add_inner_vlan is TRUE, add or modify inner VLAN tag. */
+       mxl862xx_extendedvlan_treatment_vlan_t inner_vlan;
+
+       /** Enable re-assignment of bridge port. */
+       bool reassign_bridge_port;
+       /** If b_reassign_bridge_port is TRUE, use this value for bridge port. */
+       u16 new_bridge_port_id;
+
+       /** Enable new DSCP. */
+       bool new_dscp_enable;
+       /** If b_new_dscp_enable is TRUE, use this value for DSCP. */
+       u16 new_dscp;
+
+       /** Enable new traffic class. */
+       bool new_traffic_class_enable;
+       /** If b_new_traffic_class_enable is TRUE, use this value for traffic class. */
+       u8 new_traffic_class;
+
+       /** Enable new meter. */
+       bool new_meter_enable;
+       /** New meter ID.
+
+           \remarks
+           Meter should be allocated with \ref MXL862XX_QOS_METER_ALLOC before extended
+           VLAN treatment is added. If this extended VLAN treatment is deleted,
+           this meter should be released with \ref MXL862XX_QOS_METER_FREE. */
+       u16 s_new_traffic_meter_id;
+
+       /** DSCP to PCP mapping, if
+           \ref MXL862XX_EXTENDEDVLAN_treatment_vlan_t::e_priority_mode in
+           \ref MXL862XX_EXTENDEDVLAN_treatment_t::s_outer_vlan.e_priority_mode or
+           \ref MXL862XX_EXTENDEDVLAN_treatment_t::s_inner_vlan.e_priority_mode is
+           \ref MXL862XX_EXTENDEDVLAN_TREATMENT_DSCP.
+
+           \remarks
+           The index of array stands for DSCP value. Each byte of the array is 3-bit
+           PCP value. For implementation, if DSCP2PCP is separate hardware table,
+           a resource management mechanism should be implemented. Allocation happens
+           when extended VLAN treatment added, and release happens when the
+           treatment is deleted. For debug, the DSCP2PCP table can be dumped with
+           \ref MXL862XX_DSCP2PCP_MAP_GET. */
+       u8 dscp2pcp_map[64];
+
+       /** Enable loopback. */
+       bool loopback_enable;
+       /** Enable destination/source MAC address swap. */
+       bool da_sa_swap_enable;
+       /** Enable traffic mirrored to the monitoring port. */
+       bool mirror_enable;
+} mxl862xx_extendedvlan_treatment_t;
+
+/** \brief Extended VLAN Configuration.
+    Used by \ref MXL862XX_EXTENDEDVLAN_SET and \ref MXL862XX_EXTENDEDVLAN_GET. */
+typedef struct {
+       /** This should be valid ID returned by \ref MXL862XX_EXTENDEDVLAN_ALLOC.
+           If it is \ref INVALID_HANDLE, \ref MXL862XX_EXTENDEDVLAN_config_t::n_entry_index
+           is absolute index of Extended VLAN entry in hardware for debug purpose,
+           bypassing any check. */
+       u16 extended_vlan_block_id;
+
+       /** Index of entry, ranges between 0 and
+           \ref MXL862XX_EXTENDEDVLAN_alloc_t::n_number_of_entries - 1, to
+           set (\ref MXL862XX_EXTENDEDVLAN_SET) or get (\ref MXL862XX_EXTENDEDVLAN_GET)
+           Extended VLAN Configuration entry. For debug purpose, this field could be
+           absolute index of Entended VLAN entry in hardware, when
+           \ref MXL862XX_EXTENDEDVLAN_config_t::n_extended_vlan_block_id is
+           \ref INVALID_HANDLE. */
+       u16 entry_index;
+
+       /** Extended VLAN Filter */
+       mxl862xx_extendedvlan_filter_t filter;
+       /** Extended VLAN Treatment */
+       mxl862xx_extendedvlan_treatment_t treatment;
+} mxl862xx_extendedvlan_config_t;
+
+/** \brief VLAN Filter Allocation.
+    Used by \ref MXL862XX_VLANFILTER_ALLOC and \ref MXL862XX_VLANFILTER_FREE. */
+typedef struct {
+       /** Total number of VLAN Filter entries are requested. Proper value should
+           be given for \ref MXL862XX_VLANFILTER_ALLOC. This field is ignored for
+           \ref MXL862XX_VLANFILTER_FREE. */
+       u16 number_of_entries;
+
+       /** If \ref MXL862XX_VLANFILTER_ALLOC is successful, a valid ID will be returned
+           in this field. Otherwise, \ref INVALID_HANDLE is returned in this field.
+           For \ref MXL862XX_EXTENDEDVLAN_FREE, this field should be valid ID returned by
+           \ref MXL862XX_VLANFILTER_ALLOC. */
+       u16 vlan_filter_block_id;
+
+       /** Discard packet without VLAN tag. */
+       bool discard_untagged;
+       /** Discard VLAN tagged packet not matching any filter entry. */
+       bool discard_unmatched_tagged;
+       /** Use default port VLAN ID for VLAN filtering
+
+           \remarks
+           This field is not available in PRX300. */
+       bool use_default_port_vid;
+} mxl862xx_vlanfilter_alloc_t;
+
+/** \brief VLAN Filter.
+    Used by \ref MXL862XX_VLANFILTER_SET and \ref MXL862XX_VLANFILTER_GET */
+typedef struct {
+       /** This should be valid ID return by \ref MXL862XX_VLANFILTER_ALLOC.
+           If it is \ref INVALID_HANDLE, \ref MXL862XX_VLANFILTER_config_t::n_entry_index
+           is absolute index of VLAN Filter entry in hardware for debug purpose,
+           bypassing any check. */
+       u16 vlan_filter_block_id;
+
+       /** Index of entry. ranges between 0 and
+           \ref MXL862XX_VLANFILTER_alloc_t::n_number_of_entries - 1, to
+           set (\ref MXL862XX_VLANFILTER_SET) or get (\ref MXL862XX_VLANFILTER_GET)
+           VLAN FIlter entry. For debug purpose, this field could be absolute index
+           of VLAN Filter entry in hardware, when
+           \ref MXL862XX_VLANFILTER_config_t::n_vlan_filter_block_id is
+           \ref INVALID_HANDLE. */
+       u16 entry_index;
+
+       /** VLAN TCI filter mask mode.
+
+           \remarks
+           In GSWIP-3.1, this field of first entry in the block will applies to rest
+           of entries in the same block. */
+       mxl862xx_vlan_filter_tci_mask_t vlan_filter_mask;
+
+       /** This is value for VLAN filtering. It depends on
+           \ref MXL862XX_VLANFILTER_config_t::e_vlan_filter_mask.
+           For MXL862XX_VLAN_FILTER_TCI_MASK_VID, this is 12-bit VLAN ID.
+           For MXL862XX_VLAN_FILTER_TCI_MASK_PCP, this is 3-bit PCP field of VLAN tag.
+           For MXL862XX_VLAN_FILTER_TCI_MASK_TCI, this is 16-bit TCI of VLAN tag. */
+       u32 val;
+       /** Discard packet if match. */
+       bool discard_matched;
+} mxl862xx_vlanfilter_config_t;
+
+/* VLAN Rmon Counters */
+typedef enum {
+       MXL862XX_VLAN_RMON_RX = 0,
+       MXL862XX_VLAN_RMON_TX = 1,
+       MXL862XX_VLAN_RMON__PCE_BYPASS = 2,
+} mxl862xx_vlan_rmon_type_t;
+
+/**
+ \brief RMON Counters structure for VLAN. */
+typedef struct {
+       u16 vlan_counter_index;
+       mxl862xx_vlan_rmon_type_t vlan_rmon_type;
+       u32 byte_count_high;
+       u32 byte_count_low;
+       u32 total_pkt_count;
+       u32 multicast_pkt_count;
+       u32 drop_pkt_count;
+       u32 clear_all;
+} mxl862xx_vlan_rmon_cnt_t;
+
+/**
+ \brief RMON Counters control structure for VLAN. */
+typedef struct {
+       bool vlan_rmon_enable;
+       bool include_broad_cast_pkt_counting;
+       u32 vlan_last_entry;
+} mxl862xx_vlan_rmon_control_t;
+
+/** \brief VLAN Counter Mapping. */
+typedef enum {
+       /** VLAN Mapping for Ingress */
+       MXL862XX_VLAN_MAPPING_INGRESS = 0,
+       /** VLAN Mapping for Egress */
+       MXL862XX_VLAN_MAPPING_EGRESS = 1,
+       /** VLAN Mapping for Ingress and Egress */
+       MXL862XX_VLAN_MAPPING_INGRESS_AND_EGRESS = 2
+} mxl862xx_vlan_counter_mapping_type_t;
+
+/** \brief VLAN Counter Mapping Filter. */
+typedef enum {
+       /** There is tag and criteria applies. */
+       MXL862XX_VLANCOUNTERMAP_FILTER_TYPE_NORMAL = 0,
+       /** There is tag but no criteria. */
+       MXL862XX_VLANCOUNTERMAP_FILTER_TYPE_NO_FILTER = 1,
+       /** Default entry if no other rule applies. */
+       MXL862XX_VLANCOUNTERMAP_FILTER_TYPE_DEFAULT = 2,
+       /** There is no tag. */
+       MXL862XX_VLANCOUNTERMAP_FILTER_TYPE_NO_TAG = 3,
+       /** Filter invalid*/
+       MXL862XX_VLANCOUNTERMAP_FILTER_INVALID = 4,
+} mxl862xx_vlan_counter_map_filter_type_t;
+
+/** \brief VLAN Counter Mapping Configuration. */
+typedef struct {
+       /** Counter Index */
+       u8 counter_index;
+       /** Ctp Port Id */
+       u16 ctp_port_id;
+       /** Priority Enable */
+       bool priority_enable;
+       /** Priority Val */
+       u32 priority_val;
+       /** VLAN Id Enable */
+       bool vid_enable;
+       /** VLAN Id Value */
+       u32 vid_val;
+       /** VLAN Tag Selection Value */
+       bool vlan_tag_selection_enable;
+       /** VLAN Counter Mapping Type */
+       mxl862xx_vlan_counter_mapping_type_t vlan_counter_mapping_type;
+       /** VLAN Counter Mapping Filter Type */
+       mxl862xx_vlan_counter_map_filter_type_t vlan_counter_mapping_filter_type;
+} mxl862xx_vlan_counter_mapping_config_t;
+
+/** \brief MAC Table Entry to be added.
+    Used by \ref MXL862XX_MAC_TABLE_ENTRY_ADD. */
+typedef struct {
+       /** Filtering Identifier (FID) (not supported by all switches) */
+       u16 fid;
+       /** Ethernet Port number (zero-based counting) in GSWIP-2.1/2.2/3.0. From
+           GSWIP-3.1, this field is Bridge Port ID. The valid range is hardware
+           dependent.
+
+           \remarks
+           In GSWIP-2.1/2.2/3.0, this field is used as portmap field, when the MSB
+           bit is set. In portmap mode, every value bit represents an Ethernet port.
+           LSB represents Port 0 with incrementing counting.
+           The (MSB - 1) bit represent the last port.
+           The macro \ref MXL862XX_PORTMAP_FLAG_SET allows to set the MSB bit,
+           marking it as portmap variable.
+           Checking the portmap flag can be done by
+           using the \ref MXL862XX_PORTMAP_FLAG_GET macro.
+           From GSWIP3.1, if MSB is set, other bits in this field are ignored.
+           array \ref MXL862XX_MAC_table_read_t::n_port_map is used for bit map. */
+       u32 port_id;
+       /** Bridge Port Map - to support GSWIP-3.1, following field is added
+           for port map in static entry. It's valid only when MSB of
+           \ref MXL862XX_MAC_table_read_t::n_port_id is set. Each bit stands for 1 bridge
+           port. */
+       u16 port_map[8]; /* max can be 16 */
+       /** Sub-Interface Identifier Destination (supported in GSWIP-3.0/3.1 only).
+
+           \remarks
+           In GSWIP-3.1, this field is sub interface ID for WLAN logical port. For
+           Other types, either outer VLAN ID if Nto1Vlan enabled or 0. */
+       u16 sub_if_id;
+       /** Aging Time, given in multiples of 1 second in a range
+           from 1 s to 1,000,000 s.
+           The configured value might be rounded that it fits to the given hardware platform. */
+       int age_timer;
+       /** STAG VLAN Id. Only applicable in case SVLAN support is enabled on the device. */
+       u16 vlan_id;
+       /** Static Entry (value will be aged out if the entry is not set to static). The
+           switch API implementation uses the maximum age timer in case the entry
+           is not static. */
+       bool static_entry;
+       /** Egress queue traffic class.
+           The queue index starts counting from zero.   */
+       u8 traffic_class;
+       /** MAC Address to add to the table. */
+       u8 mac[ETH_ALEN];
+       /** Source/Destination MAC address filtering flag (GSWIP-3.1 only)
+           Value 0 - not filter, 1 - source address filter,
+           2 - destination address filter, 3 - both source and destination filter.
+
+           \remarks
+           Please refer to "GSWIP Hardware Architecture Spec" chapter 3.4.4.6
+           "Source MAC Address Filtering and Destination MAC Address Filtering"
+           for more detail. */
+       u8 filter_flag;
+       /** Packet is marked as IGMP controlled if destination MAC address matches
+           MAC in this entry. (GSWIP-3.1 only) */
+       bool igmp_controlled;
+
+       /** Associated Mac address -(GSWIP-3.2)*/
+       u8 associated_mac[ETH_ALEN];
+
+       /** TCI for (GSWIP-3.2) B-Step
+           Bit [0:11] - VLAN ID
+           Bit [12] - VLAN CFI/DEI
+           Bit [13:15] - VLAN PRI */
+       u16 tci;
+} mxl862xx_mac_table_add_t;
+
+/** \brief MAC Table Entry to be read.
+    Used by \ref MXL862XX_MAC_TABLE_ENTRY_READ. */
+typedef struct {
+       /** Restart the get operation from the beginning of the table. Otherwise
+           return the next table entry (next to the entry that was returned
+           during the previous get operation). This boolean parameter is set by the
+           calling application. */
+       bool initial;
+       /** Indicates that the read operation got all last valid entries of the
+           table. This boolean parameter is set by the switch API
+           when the Switch API is called after the last valid one was returned already. */
+       bool last;
+       /** Get the MAC table entry belonging to the given Filtering Identifier
+           (not supported by all switches). */
+       u16 fid;
+       /** Ethernet Port number (zero-based counting) in GSWIP-2.1/2.2/3.0. From
+           GSWIP-3.1, this field is Bridge Port ID. The valid range is hardware
+           dependent.
+
+           \remarks
+           In GSWIP-2.1/2.2/3.0, this field is used as portmap field, when the MSB
+           bit is set. In portmap mode, every value bit represents an Ethernet port.
+           LSB represents Port 0 with incrementing counting.
+           The (MSB - 1) bit represent the last port.
+           The macro \ref MXL862XX_PORTMAP_FLAG_SET allows to set the MSB bit,
+           marking it as portmap variable.
+           Checking the portmap flag can be done by
+           using the \ref MXL862XX_PORTMAP_FLAG_GET macro.
+           From GSWIP3.1, if MSB is set, other bits in this field are ignored.
+           array \ref MXL862XX_MAC_table_read_t::n_port_map is used for bit map. */
+       u32 port_id;
+       /** Bridge Port Map - to support GSWIP-3.1, following field is added
+           for port map in static entry. It's valid only when MSB of
+           \ref MXL862XX_MAC_table_read_t::n_port_id is set. Each bit stands for 1 bridge
+           port. */
+       u16 port_map[8]; /* max can be 16 */
+       /** Aging Time, given in multiples of 1 second in a range from 1 s to 1,000,000 s.
+           The value read back in a GET command might differ slightly from the value
+           given in the SET command due to limited hardware timing resolution.
+           Filled out by the switch API implementation. */
+       int age_timer;
+       /** STAG VLAN Id. Only applicable in case SVLAN support is enabled on the device. */
+       u16 vlan_id;
+       /** Static Entry (value will be aged out after 'n_age_timer' if the entry
+           is not set to static). */
+       bool static_entry;
+       /** Sub-Interface Identifier Destination (supported in GSWIP-3.0/3.1 only). */
+       u16 sub_if_id;
+       /** MAC Address. Filled out by the switch API implementation. */
+       u8 mac[ETH_ALEN];
+       /** Source/Destination MAC address filtering flag (GSWIP-3.1 only)
+           Value 0 - not filter, 1 - source address filter,
+           2 - destination address filter, 3 - both source and destination filter.
+
+           \remarks
+           Please refer to "GSWIP Hardware Architecture Spec" chapter 3.4.4.6
+           "Source MAC Address Filtering and Destination MAC Address Filtering"
+           for more detail. */
+       u8 filter_flag;
+       /** Packet is marked as IGMP controlled if destination MAC address matches
+           MAC in this entry. (GSWIP-3.1 only) */
+       bool igmp_controlled;
+
+       /** Changed
+       0: the entry is not changed
+       1: the entry is changed and not accessed yet */
+
+       bool entry_changed;
+
+       /** Associated Mac address -(GSWIP-3.2)*/
+       u8 associated_mac[ETH_ALEN];
+       /* MAC Table Hit Status Update (Supported in GSWip-3.1/3.2) */
+       bool hit_status;
+       /** TCI for (GSWIP-3.2) B-Step
+           Bit [0:11] - VLAN ID
+           Bit [12] - VLAN CFI/DEI
+           Bit [13:15] - VLAN PRI */
+       u16 tci;
+       u16 first_bridge_port_id;
+} mxl862xx_mac_table_read_t;
+
+/** \brief MAC Table Entry to be removed.
+    Used by \ref MXL862XX_MAC_TABLE_ENTRY_REMOVE. */
+typedef struct {
+       /** Filtering Identifier (FID) (not supported by all switches) */
+       u16 fid;
+       /** MAC Address to be removed from the table. */
+       u8 mac[ETH_ALEN];
+       /** Source/Destination MAC address filtering flag (GSWIP-3.1 only)
+           Value 0 - not filter, 1 - source address filter,
+           2 - destination address filter, 3 - both source and destination filter.
+
+           \remarks
+           Please refer to "GSWIP Hardware Architecture Spec" chapter 3.4.4.6
+           "Source MAC Address Filtering and Destination MAC Address Filtering"
+           for more detail. */
+       u8 filter_flag;
+       /** TCI for (GSWIP-3.2) B-Step
+           Bit [0:11] - VLAN ID
+           Bit [12] - VLAN CFI/DEI
+           Bit [13:15] - VLAN PRI */
+       u16 tci;
+} mxl862xx_mac_table_remove_t;
+
+/** \brief Bridge configuration mask.
+    Used by \ref MXL862XX_BRIDGE_config_t. */
+typedef enum {
+       /** Mask for \ref MXL862XX_BRIDGE_config_t::b_mac_learning_limit_enable
+           and \ref MXL862XX_BRIDGE_config_t::n_mac_learning_limit. */
+       MXL862XX_BRIDGE_CONFIG_MASK_MAC_LEARNING_LIMIT = 0x00000001,
+       /** Mask for \ref MXL862XX_BRIDGE_config_t::n_mac_learning_count */
+       MXL862XX_BRIDGE_CONFIG_MASK_MAC_LEARNED_COUNT = 0x00000002,
+       /** Mask for \ref MXL862XX_BRIDGE_config_t::n_learning_discard_event */
+       MXL862XX_BRIDGE_CONFIG_MASK_MAC_DISCARD_COUNT = 0x00000004,
+       /** Mask for \ref MXL862XX_BRIDGE_config_t::b_sub_metering_enable and
+           \ref MXL862XX_BRIDGE_config_t::n_traffic_sub_meter_id */
+       MXL862XX_BRIDGE_CONFIG_MASK_SUB_METER = 0x00000008,
+       /** Mask for \ref MXL862XX_BRIDGE_config_t::e_forward_broadcast,
+           \ref MXL862XX_BRIDGE_config_t::e_forward_unknown_multicast_ip,
+           \ref MXL862XX_BRIDGE_config_t::e_forward_unknown_multicast_non_ip,
+           and \ref MXL862XX_BRIDGE_config_t::e_forward_unknown_unicast. */
+       MXL862XX_BRIDGE_CONFIG_MASK_FORWARDING_MODE = 0x00000010,
+
+       /** Enable all */
+       MXL862XX_BRIDGE_CONFIG_MASK_ALL = 0x7FFFFFFF,
+       /** Bypass any check for debug purpose */
+       MXL862XX_BRIDGE_CONFIG_MASK_FORCE = 0x80000000
+} mxl862xx_bridge_config_mask_t;
+
+/** \brief Bridge forwarding type of packet.
+    Used by \ref MeXL862XX_BRIDGE_port_config_t. */
+typedef enum {
+       /** Packet is flooded to port members of ingress bridge port */
+       MXL862XX_BRIDGE_FORWARD_FLOOD = 0,
+       /** Packet is dscarded */
+       MXL862XX_BRIDGE_FORWARD_DISCARD = 1,
+       /** Packet is forwarded to logical port 0 CTP port 0 bridge port 0 */
+       MXL862XX_BRIDGE_FORWARD_CPU = 2
+} mxl862xx_bridge_forward_mode_t;
+
+/** \brief Bridge Configuration.
+    Used by \ref MXL862XX_BRIDGE_CONFIG_SET and \ref MXL862XX_BRIDGE_CONFIG_GET. */
+typedef struct {
+       /** Bridge ID (FID) allocated by \ref MXL862XX_BRIDGE_ALLOC.
+
+           \remarks
+           If \ref MXL862XX_BRIDGE_config_t::e_mask has
+           \ref MXL862XX_Bridge_config_mask_t::MXL862XX_BRIDGE_CONFIG_MASK_FORCE, this field is
+           absolute index of Bridge (FID) in hardware for debug purpose, bypassing
+           any check. */
+       u16 bridge_id;
+
+       /** Mask for updating/retrieving fields. */
+       mxl862xx_bridge_config_mask_t mask;
+
+       /** Enable MAC learning limitation. */
+       bool mac_learning_limit_enable;
+       /** Max number of MAC can be learned in this bridge (all bridge ports). */
+       u16 mac_learning_limit;
+
+       /** Get number of MAC address learned from this bridge port. */
+       u16 mac_learning_count;
+
+       /** Number of learning discard event due to hardware resource not available.
+
+           \remarks
+           This is discard event due to either MAC table full or Hash collision.
+           Discard due to n_mac_learning_count reached is not counted in this field. */
+       u32 learning_discard_event;
+
+       /** Traffic metering on type of traffic (such as broadcast, multicast,
+           unknown unicast, etc) applies. */
+       bool sub_metering_enable[MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX];
+       /** Meter for bridge process with specific type (such as broadcast,
+           multicast, unknown unicast, etc). Need pre-allocated for each type. */
+       u16 traffic_sub_meter_id[MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX];
+
+       /** Forwarding mode of broadcast traffic. */
+       mxl862xx_bridge_forward_mode_t forward_broadcast;
+       /** Forwarding mode of unknown multicast IP traffic. */
+       mxl862xx_bridge_forward_mode_t forward_unknown_multicast_ip;
+       /** Forwarding mode of unknown multicast non-IP traffic. */
+       mxl862xx_bridge_forward_mode_t forward_unknown_multicast_non_ip;
+       /** Forwarding mode of unknown unicast traffic. */
+       mxl862xx_bridge_forward_mode_t forward_unknown_unicast;
+} mxl862xx_bridge_config_t;
+
+/** \brief Aging Timer Value.
+    Used by \ref mxl862xx_cfg_t. */
+typedef enum {
+       /** 1 second aging time */
+       MXL862XX_AGETIMER_1_SEC = 1,
+       /** 10 seconds aging time */
+       MXL862XX_AGETIMER_10_SEC        = 2,
+       /** 300 seconds aging time */
+       MXL862XX_AGETIMER_300_SEC       = 3,
+       /** 1 hour aging time */
+       MXL862XX_AGETIMER_1_HOUR        = 4,
+       /** 24 hours aging time */
+       MXL862XX_AGETIMER_1_DAY = 5,
+       /** Custom aging time in seconds */
+       MXL862XX_AGETIMER_CUSTOM  = 6
+} mxl862xx_age_timer_t;
+
+/** \brief Global Switch configuration Attributes.
+    Used by \ref MXL862XX_CFG_SET and \ref MXL862XX_CFG_GET. */
+typedef struct {
+       /** MAC table aging timer. After this timer expires the MAC table
+           entry is aged out. */
+       mxl862xx_age_timer_t mac_table_age_timer;
+       /** If eMAC_TableAgeTimer = MXL862XX_AGETIMER_CUSTOM, this variable defines
+           MAC table aging timer in seconds. */
+       u32 age_timer;
+       /** Maximum Ethernet packet length. */
+       u16 max_packet_len;
+       /** Automatic MAC address table learning limitation consecutive action.
+           These frame addresses are not learned, but there exists control as to whether
+           the frame is still forwarded or dropped.
+
+           - False: Drop
+           - True: Forward
+       */
+       bool learning_limit_action;
+       /** Accept or discard MAC port locking violation packets.
+           MAC spoofing detection features identifies ingress packets that carry
+           a MAC source address which was previously learned on a different
+           ingress port (learned by MAC bridging table). This also applies to
+           static added entries. MAC address port locking is configured on
+           port level by 'bLearningMAC_PortLock'.
+
+           - False: Drop
+                - True: Forward
+       */
+       bool mac_locking_action;
+       /** Accept or discard MAC spoofing and port MAC locking violation packets.
+           MAC spoofing detection features identifies ingress packets that carry
+           a MAC source address which was previously learned on a different
+           ingress port (learned by MAC bridging table). This also applies to
+           static added entries. MAC spoofing detection is enabled on port
+           level by 'bMAC_SpoofingDetection'.
+
+           - False: Drop
+           - True: Forward
+       */
+       bool mac_spoofing_action;
+       /** Pause frame MAC source address mode. If enabled, use the alternative
+           address specified with 'nMAC'. */
+       bool pause_mac_mode_src;
+       /** Pause frame MAC source address. */
+       u8      pause_mac_src[ETH_ALEN];
+} mxl862xx_cfg_t;
+
+/** \brief Sets the portmap flag of a port_iD variable.
+    Some Switch API commands allow to use a port index as portmap variable.
+    This requires that the MSB bit is set to indicate that this variable
+    contains a portmap, instead of a port index.
+    In portmap mode, every value bit represents an Ethernet port.
+    LSB represents Port 0 with incrementing counting.
+    The (MSB - 1) bit represent the last port. */
+#define MXL862XX_PORTMAP_FLAG_SET(var_type) (1 << (sizeof(((var_type *)0)->port_id) * 8 - 1))
+
+/** \brief Checks the portmap flag of a port_iD variable.
+    Some Switch API commands allow to use a port index as portmap variable.
+    This requires that the MSB bit is set to indicate that this variable
+    contains a portmap, instead of a port index.
+    In portmap mode, every value bit represents an Ethernet port.
+    LSB represents Port 0 with incrementing counting.
+    The (MSB - 1) bit represent the last port. */
+#define MXL862XX_PORTMAP_FLAG_GET(var_type) (1 << (sizeof(((var_type *)0)->port_id) * 8 - 1))
+
+#pragma scalar_storage_order default
+#pragma pack(pop)
+
+#include "mxl862xx_ctp.h"
+#include "mxl862xx_flow.h"
+
+struct sys_fw_image_version {
+       uint8_t iv_major;
+       uint8_t iv_minor;
+       uint16_t iv_revision;
+       uint32_t iv_build_num;
+};
+
+int sys_misc_fw_version(const mxl862xx_device_t *dummy,
+                       struct sys_fw_image_version *sys_img_ver);
+
+int mxl862xx_register_mod(const mxl862xx_device_t *, mxl862xx_register_mod_t *);
+int mxl862xx_port_link_cfg_set(const mxl862xx_device_t *, mxl862xx_port_link_cfg_t *);
+int mxl862xx_port_link_cfg_get(const mxl862xx_device_t *, mxl862xx_port_link_cfg_t *);
+int mxl862xx_port_cfg_set(const mxl862xx_device_t *, mxl862xx_port_cfg_t *);
+int mxl862xx_port_cfg_get(const mxl862xx_device_t *, mxl862xx_port_cfg_t *);
+/* Bridge */
+int mxl862xx_bridge_alloc(const mxl862xx_device_t *, mxl862xx_bridge_alloc_t *);
+int mxl862xx_bridge_free(const mxl862xx_device_t *, mxl862xx_bridge_alloc_t *);
+int mxl862xx_bridge_config_set(const mxl862xx_device_t *dev, mxl862xx_bridge_config_t *);
+int mxl862xx_bridge_config_get(const mxl862xx_device_t *dev, mxl862xx_bridge_config_t *);
+/* Bridge Port */
+int mxl862xx_bridge_port_alloc(const mxl862xx_device_t *,
+                                        mxl862xx_bridge_port_alloc_t *);
+int mxl862xx_bridge_port_free(const mxl862xx_device_t *,
+                                        mxl862xx_bridge_port_alloc_t *);
+int mxl862xx_bridge_port_config_set(const mxl862xx_device_t *,
+                                   mxl862xx_bridge_port_config_t *);
+int mxl862xx_bridge_port_config_get(const mxl862xx_device_t *,
+                                   mxl862xx_bridge_port_config_t *);
+/* Debug */
+int mxl862xx_debug_rmon_port_get(const mxl862xx_device_t *,
+                                mxl862xx_debug_rmon_port_cnt_t *);
+
+int mxl862xx_mac_table_clear(const mxl862xx_device_t *);
+int mxl862xx_mac_table_clear_cond(const mxl862xx_device_t *,
+                                 mxl862xx_mac_table_clear_cond_t *);
+int mxl862xx_mac_table_entry_read(const mxl862xx_device_t *, mxl862xx_mac_table_read_t *);
+int mxl862xx_mac_table_entry_add(const mxl862xx_device_t *, mxl862xx_mac_table_add_t *);
+int mxl862xx_mac_table_entry_remove(const mxl862xx_device_t *,
+              mxl862xx_mac_table_remove_t *);
+int mxl862xx_stp_port_cfg_set(const mxl862xx_device_t *, mxl862xx_stp_port_cfg_t *parm);
+
+int mxl862xx_ss_sp_tag_get(const mxl862xx_device_t *, mxl862xx_ss_sp_tag_t *);
+int mxl862xx_ss_sp_tag_set(const mxl862xx_device_t *, mxl862xx_ss_sp_tag_t *);
+
+int mxl862xx_ctp_port_config_get(const mxl862xx_device_t *, mxl862xx_ctp_port_config_t *);
+int mxl862xx_ctp_port_config_set(const mxl862xx_device_t *, mxl862xx_ctp_port_config_t *);
+int mxl862xx_ctp_port_assignment_set(const mxl862xx_device_t *,
+                                    mxl862xx_ctp_port_assignment_t *);
+int mxl862xx_ctp_port_assignment_get(const mxl862xx_device_t *,
+                                    mxl862xx_ctp_port_assignment_t *);
+int mxl862xx_monitor_port_cfg_get(const mxl862xx_device_t *, mxl862xx_monitor_port_cfg_t *);
+int mxl862xx_monitor_port_cfg_set(const mxl862xx_device_t *, mxl862xx_monitor_port_cfg_t *);
+
+int mxl862xx_extended_vlan_alloc(const mxl862xx_device_t *,
+                                mxl862xx_extendedvlan_alloc_t *);
+int mxl862xx_extended_vlan_set(const mxl862xx_device_t *,
+                              mxl862xx_extendedvlan_config_t *);
+int mxl862xx_extended_vlan_get(const mxl862xx_device_t *,
+                              mxl862xx_extendedvlan_config_t *);
+int mxl862xx_extended_vlan_free(const mxl862xx_device_t *,
+                               mxl862xx_extendedvlan_alloc_t *);
+int mxl862xx_vlan_filter_alloc(const mxl862xx_device_t *, mxl862xx_vlanfilter_alloc_t *);
+int mxl862xx_vlan_filter_set(const mxl862xx_device_t *, mxl862xx_vlanfilter_config_t *);
+int mxl862xx_vlan_filter_get(const mxl862xx_device_t *, mxl862xx_vlanfilter_config_t *);
+int mxl862xx_vlan_filter_free(const mxl862xx_device_t *, mxl862xx_vlanfilter_alloc_t *);
+int mxl862xx_cfg_get(const mxl862xx_device_t *, mxl862xx_cfg_t *);
+int mxl862xx_cfg_set(const mxl862xx_device_t *, mxl862xx_cfg_t *);
+#endif /* _MXL862XX_API_H_ */
diff --git a/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_ctp.h b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_ctp.h
new file mode 100644 (file)
index 0000000..683fa8f
--- /dev/null
@@ -0,0 +1,336 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * drivers/net/dsa/host_api/mxl862xx_ctp.h - Header file for DSA Driver for MaxLinear Mxl862xx switch chips family
+ *
+ * Copyright (C) 2024 MaxLinear Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+#ifndef _MXL862XX_CTP_H_
+#define _MXL862XX_CTP_H_
+
+#include "mxl862xx_types.h"
+
+#pragma pack(push, 1)
+#pragma scalar_storage_order little-endian
+
+/** \brief Logical port mode.
+    Used by \ref MXL862XX_CTP_port_assignment_t. */
+typedef enum {
+       /** WLAN with 8-bit station ID */
+       MXL862XX_LOGICAL_PORT_8BIT_WLAN = 0,
+       /** WLAN with 9-bit station ID */
+       MXL862XX_LOGICAL_PORT_9BIT_WLAN = 1,
+       /** GPON OMCI context */
+       MXL862XX_LOGICAL_PORT_GPON = 2,
+       /** EPON context */
+       MXL862XX_LOGICAL_PORT_EPON = 3,
+       /** G.INT context */
+       MXL862XX_LOGICAL_PORT_GINT = 4,
+       /** Others (sub interface ID is 0 by default) */
+       MXL862XX_LOGICAL_PORT_OTHER = 0xFF,
+} mxl862xx_logical_port_mode_t;
+
+/** \brief CTP Port configuration mask.
+    Used by \ref MXL862XX_CTP_port_config_t. */
+typedef enum {
+       /** Mask for \ref MXL862XX_CTP_port_config_t::bridge_port_id */
+       MXL862XX_CTP_PORT_CONFIG_MASK_BRIDGE_PORT_ID = 0x00000001,
+       /** Mask for \ref MXL862XX_CTP_port_config_t::b_forced_traffic_class and
+           \ref MXL862XX_CTP_port_config_t::n_default_traffic_class */
+       MXL862XX_CTP_PORT_CONFIG_MASK_FORCE_TRAFFIC_CLASS = 0x00000002,
+       /** Mask for \ref MXL862XX_CTP_port_config_t::b_ingress_extended_vlan_enable and
+           \ref MXL862XX_CTP_port_config_t::n_ingress_extended_vlan_block_id */
+       MXL862XX_CTP_PORT_CONFIG_MASK_INGRESS_VLAN = 0x00000004,
+       /** Mask for \ref MXL862XX_CTP_port_config_t::b_ingress_extended_vlan_igmp_enable and
+           \ref MXL862XX_CTP_port_config_t::n_ingress_extended_vlan_block_id_igmp */
+       MXL862XX_CTP_PORT_CONFIG_MASK_INGRESS_VLAN_IGMP = 0x00000008,
+       /** Mask for \ref MXL862XX_CTP_port_config_t::b_egress_extended_vlan_enable and
+           \ref MXL862XX_CTP_port_config_t::n_egress_extended_vlan_block_id */
+       MXL862XX_CTP_PORT_CONFIG_MASK_EGRESS_VLAN = 0x00000010,
+       /** Mask for \ref MXL862XX_CTP_port_config_t::b_egress_extended_vlan_igmp_enable and
+           \ref MXL862XX_CTP_port_config_t::n_egress_extended_vlan_block_id_igmp */
+       MXL862XX_CTP_PORT_CONFIG_MASK_EGRESS_VLAN_IGMP = 0x00000020,
+       /** Mask for \ref MXL862XX_CTP_port_config_t::b_ingress_nto1Vlan_enable */
+       MXL862XX_CTP_PORT_CONFIG_MASK_INRESS_NTO1_VLAN = 0x00000040,
+       /** Mask for \ref MXL862XX_CTP_port_config_t::b_egress_nto1Vlan_enable */
+       MXL862XX_CTP_PORT_CONFIG_MASK_EGRESS_NTO1_VLAN = 0x00000080,
+       /** Mask for \ref MXL862XX_CTP_port_config_t::e_ingress_marking_mode */
+       MXL862XX_CTP_PORT_CONFIG_INGRESS_MARKING = 0x00000100,
+       /** Mask for \ref MXL862XX_CTP_port_config_t::e_egress_marking_mode */
+       MXL862XX_CTP_PORT_CONFIG_EGRESS_MARKING = 0x00000200,
+       /** Mask for \ref MXL862XX_CTP_port_config_t::b_egress_marking_override_enable and
+           \ref MXL862XX_CTP_port_config_t::e_egress_marking_mode_override */
+       MXL862XX_CTP_PORT_CONFIG_EGRESS_MARKING_OVERRIDE = 0x00000400,
+       /** Mask for \ref MXL862XX_CTP_port_config_t::e_egress_remarking_mode */
+       MXL862XX_CTP_PORT_CONFIG_EGRESS_REMARKING = 0x00000800,
+       /** Mask for \ref MXL862XX_CTP_port_config_t::b_ingress_metering_enable and
+           \ref MXL862XX_CTP_port_config_t::n_ingress_traffic_meter_id */
+       MXL862XX_CTP_PORT_CONFIG_INGRESS_METER = 0x00001000,
+       /** Mask for \ref MXL862XX_CTP_port_config_t::b_egress_metering_enable and
+           \ref MXL862XX_CTP_port_config_t::n_egress_traffic_meter_id */
+       MXL862XX_CTP_PORT_CONFIG_EGRESS_METER = 0x00002000,
+       /** Mask for \ref MXL862XX_CTP_port_config_t::b_bridging_bypass,
+           \ref MXL862XX_CTP_port_config_t::n_dest_logical_port_id,
+           \ref MXL862XX_CTP_port_config_t::b_pmapper_enable,
+           \ref MXL862XX_CTP_port_config_t::n_dest_sub_if_id_group,
+           \ref MXL862XX_CTP_port_config_t::e_pmapper_mapping_mode
+           \ref mxl862xx_bridge_port_config_t::b_pmapper_id_valid and
+           \ref MXL862XX_CTP_port_config_t::s_pmapper */
+       MXL862XX_CTP_PORT_CONFIG_BRIDGING_BYPASS = 0x00004000,
+       /** Mask for \ref MXL862XX_CTP_port_config_t::n_first_flow_entry_index and
+           \ref MXL862XX_CTP_port_config_t::n_number_of_flow_entries */
+       MXL862XX_CTP_PORT_CONFIG_FLOW_ENTRY = 0x00008000,
+       /** Mask for \ref MXL862XX_CTP_port_config_t::b_ingress_loopback_enable,
+           \ref MXL862XX_CTP_port_config_t::b_ingress_da_sa_swap_enable,
+           \ref MXL862XX_CTP_port_config_t::b_egress_loopback_enable,
+           \ref MXL862XX_CTP_port_config_t::b_egress_da_sa_swap_enable,
+           \ref MXL862XX_CTP_port_config_t::b_ingress_mirror_enable and
+           \ref MXL862XX_CTP_port_config_t::b_egress_mirror_enable */
+       MXL862XX_CTP_PORT_CONFIG_LOOPBACK_AND_MIRROR = 0x00010000,
+
+       /** Enable all fields */
+       MXL862XX_CTP_PORT_CONFIG_MASK_ALL = 0x7FFFFFFF,
+       /** Bypass any check for debug purpose */
+       MXL862XX_CTP_PORT_CONFIG_MASK_FORCE = 0x80000000
+} mxl862xx_ctp_port_config_mask_t;
+
+/** \brief CTP Port Assignment/association with logical port.
+    Used by \ref MXL862XX_CTP_PORT_ASSIGNMENT_ALLOC, \ref MXL862XX_CTP_PORT_ASSIGNMENT_SET
+    and \ref MXL862XX_CTP_PORT_ASSIGNMENT_GET. */
+typedef struct {
+       /** Logical Port Id. The valid range is hardware dependent. */
+       u8 logical_port_id;
+
+       /** First CTP Port ID mapped to above logical port ID.
+
+           \remarks
+           For \ref MXL862XX_CTP_PORT_ASSIGNMENT_ALLOC, this is output when CTP
+           allocation is successful. For other APIs, this is input. */
+       u16 first_ctp_port_id;
+       /** Total number of CTP Ports mapped above logical port ID. */
+       u16 number_of_ctp_port;
+
+       /** Logical port mode to define sub interface ID format. */
+       mxl862xx_logical_port_mode_t mode;
+
+       /** Bridge ID (FID)
+
+           \remarks
+           For \ref MXL862XX_CTP_PORT_ASSIGNMENT_ALLOC, this is input. Each CTP allocated
+           is mapped to Bridge Port given by this field. The Bridge Port will be
+           configured to use first CTP
+           (\ref MXL862XX_CTP_port_assignment_t::n_first_ctp_port_id) as egress CTP.
+           For other APIs, this is ignored. */
+       u16 bridge_port_id;
+} mxl862xx_ctp_port_assignment_t;
+
+/** \brief CTP Port Configuration.
+    Used by \ref MXL862XX_CTP_PORT_CONFIG_SET and \ref MXL862XX_CTP_PORT_CONFIG_GET. */
+typedef struct {
+       /** Logical Port Id. The valid range is hardware dependent.
+           If \ref MXL862XX_CTP_port_config_t::e_mask has
+           \ref MXL862XX_Ctp_port_config_mask_t::MXL862XX_CTP_PORT_CONFIG_MASK_FORCE, this field
+           is ignored. */
+       u8 logical_port_id;
+
+       /** Sub interface ID group. The valid range is hardware/protocol dependent.
+
+           \remarks
+           Sub interface ID group is defined for each of \ref MXL862XX_Logical_port_mode_t.
+           For both \ref MXL862XX_LOGICAL_PORT_8BIT_WLAN and
+           \ref MXL862XX_LOGICAL_PORT_9BIT_WLAN, this field is VAP.
+           For \ref MXL862XX_LOGICAL_PORT_GPON, this field is GEM index.
+           For \ref MXL862XX_LOGICAL_PORT_EPON, this field is stream index.
+           For \ref MXL862XX_LOGICAL_PORT_GINT, this field is LLID.
+           For others, this field is 0.
+           If \ref MXL862XX_CTP_port_config_t::e_mask has
+           \ref MXL862XX_Ctp_port_config_mask_t::MXL862XX_CTP_PORT_CONFIG_MASK_FORCE, this field
+           is absolute index of CTP in hardware for debug purpose, bypassing
+           any check. */
+       u16 n_sub_if_id_group;
+
+       /** Mask for updating/retrieving fields. */
+       mxl862xx_ctp_port_config_mask_t mask;
+
+       /** Ingress Bridge Port ID to which this CTP port is associated for ingress
+           traffic. */
+       u16 bridge_port_id;
+
+       /** Default traffic class can not be overridden by other rules (except
+           traffic flow table and special tag) in processing stages. */
+       bool forced_traffic_class;
+       /** Default traffic class associated with all ingress traffic from this CTP
+           Port. */
+       u8 default_traffic_class;
+
+       /** Enable Extended VLAN processing for ingress non-IGMP traffic. */
+       bool ingress_extended_vlan_enable;
+       /** Extended VLAN block allocated for ingress non-IGMP traffic. It defines
+           extended VLAN process for ingress non-IGMP traffic. Valid when
+           b_ingress_extended_vlan_enable is TRUE. */
+       u16 ingress_extended_vlan_block_id;
+       /** Extended VLAN block size for ingress non-IGMP traffic. This is optional.
+           If it is 0, the block size of n_ingress_extended_vlan_block_id will be used.
+           Otherwise, this field will be used. */
+       u16 ingress_extended_vlan_block_size;
+       /** Enable extended VLAN processing for ingress IGMP traffic. */
+       bool ingress_extended_vlan_igmp_enable;
+       /** Extended VLAN block allocated for ingress IGMP traffic. It defines
+           extended VLAN process for ingress IGMP traffic. Valid when
+           b_ingress_extended_vlan_igmp_enable is TRUE. */
+       u16 ingress_extended_vlan_block_id_igmp;
+       /** Extended VLAN block size for ingress IGMP traffic. This is optional.
+           If it is 0, the block size of n_ingress_extended_vlan_block_id_igmp will be
+           used. Otherwise, this field will be used. */
+       u16 ingress_extended_vlan_block_size_igmp;
+
+       /** Enable extended VLAN processing for egress non-IGMP traffic. */
+       bool egress_extended_vlan_enable;
+       /** Extended VLAN block allocated for egress non-IGMP traffic. It defines
+           extended VLAN process for egress non-IGMP traffic. Valid when
+           b_egress_extended_vlan_enable is TRUE. */
+       u16 egress_extended_vlan_block_id;
+       /** Extended VLAN block size for egress non-IGMP traffic. This is optional.
+           If it is 0, the block size of n_egress_extended_vlan_block_id will be used.
+           Otherwise, this field will be used. */
+       u16 egress_extended_vlan_block_size;
+       /** Enable extended VLAN processing for egress IGMP traffic. */
+       bool egress_extended_vlan_igmp_enable;
+       /** Extended VLAN block allocated for egress IGMP traffic. It defines
+           extended VLAN process for egress IGMP traffic. Valid when
+           b_egress_extended_vlan_igmp_enable is TRUE. */
+       u16 egress_extended_vlan_block_id_igmp;
+       /** Extended VLAN block size for egress IGMP traffic. This is optional.
+           If it is 0, the block size of n_egress_extended_vlan_block_id_igmp will be
+           used. Otherwise, this field will be used. */
+       u16 egress_extended_vlan_block_size_igmp;
+
+       /** For WLAN type logical port, this should be FALSE. For other types, if
+            enabled and ingress packet is VLAN tagged, outer VLAN ID is used for
+           "n_sub_if_id" field in MAC table, otherwise, 0 is used for "n_sub_if_id". */
+       bool ingress_nto1vlan_enable;
+       /** For WLAN type logical port, this should be FALSE. For other types, if
+            enabled and egress packet is known unicast, outer VLAN ID is from
+            "n_sub_if_id" field in MAC table. */
+       bool egress_nto1vlan_enable;
+
+       /** Ingress color marking mode for ingress traffic. */
+       mxl862xx_color_marking_mode_t ingress_marking_mode;
+       /** Egress color marking mode for ingress traffic at egress priority queue
+           color marking stage */
+       mxl862xx_color_marking_mode_t egress_marking_mode;
+       /** Egress color marking mode override color marking mode from last stage. */
+       bool egress_marking_override_enable;
+       /** Egress color marking mode for egress traffic. Valid only when
+           b_egress_marking_override is TRUE. */
+       mxl862xx_color_marking_mode_t egress_marking_mode_override;
+
+       /** Color remarking for egress traffic. */
+       mxl862xx_color_remarking_mode_t egress_remarking_mode;
+
+       /** Traffic metering on ingress traffic applies. */
+       bool ingress_metering_enable;
+       /** Meter for ingress CTP process.
+
+           \remarks
+           Meter should be allocated with \ref MXL862XX_QOS_METER_ALLOC before CTP
+           port configuration. If this CTP port is re-set, the last used meter
+           should be released. */
+       u16 ingress_traffic_meter_id;
+       /** Traffic metering on egress traffic applies. */
+       bool egress_metering_enable;
+       /** Meter for egress CTP process.
+
+           \remarks
+           Meter should be allocated with \ref MXL862XX_QOS_METER_ALLOC before CTP
+           port configuration. If this CTP port is re-set, the last used meter
+           should be released. */
+       u16 egress_traffic_meter_id;
+
+       /** Ingress traffic bypass bridging/multicast processing. Following
+           parameters are used to determine destination. Traffic flow table is not
+           bypassed. */
+       bool bridging_bypass;
+       /** When b_bridging_bypass is TRUE, this field defines destination logical
+           port. */
+       u8 dest_logical_port_id;
+       /** When b_bridging_bypass is TRUE, this field indicates whether to use
+           \ref MXL862XX_CTP_port_config_t::n_dest_sub_if_id_group or
+           \ref MXL862XX_CTP_port_config_t::e_pmapper_mapping_mode/
+           \ref MXL862XX_CTP_port_config_t::s_pmapper. */
+       bool pmapper_enable;
+       /** When b_bridging_bypass is TRUE and b_pmapper_enable is FALSE, this field
+           defines destination sub interface ID group. */
+       u16 dest_sub_if_id_group;
+       /** When b_bridging_bypass is TRUE and b_pmapper_enable is TRUE, this field
+           selects either DSCP or PCP to derive sub interface ID. */
+       mxl862xx_pmapper_mapping_mode_t pmapper_mapping_mode;
+       /** When b_pmapper_enable is TRUE, P-mapper is used. This field determines
+           whether s_pmapper.n_pmapper_id is valid. If this field is TRUE, the
+           P-mapper is re-used and no allocation of new P-mapper or value
+           change in the P-mapper. If this field is FALSE, allocation is
+           taken care in the API implementation. */
+       bool pmapper_id_valid;
+       /** When b_bridging_bypass is TRUE and b_pmapper_enable is TRUE, P-mapper is
+           used. If b_pmapper_id_valid is FALSE, API implementation need take care
+           of P-mapper allocation, and maintain the reference counter of
+           P-mapper used multiple times. If b_pmapper_id_valid is TRUE, only
+           s_pmapper.n_pmapper_id is used to associate the P-mapper, and there is
+           no allocation of new P-mapper or value change in the P-mapper. */
+       mxl862xx_pmapper_t pmapper;
+
+       /** First traffic flow table entry is associated to this CTP port. Ingress
+           traffic from this CTP port will go through traffic flow table search
+           starting from n_first_flow_entry_index. Should be times of 4. */
+       u16 first_flow_entry_index;
+       /** Number of traffic flow table entries are associated to this CTP port.
+           Ingress traffic from this CTP port will go through PCE rules search
+           ending at (n_first_flow_entry_index+n_number_of_flow_entries)-1. Should
+           be times of 4. */
+       u16 number_of_flow_entries;
+
+       /** Ingress traffic from this CTP port will be redirected to ingress
+           logical port of this CTP port with source sub interface ID used as
+           destination sub interface ID. Following processing except traffic
+           flow table search is bypassed if loopback enabled. */
+       bool ingress_loopback_enable;
+       /** Destination/Source MAC address of ingress traffic is swapped before
+           transmitted (not swapped during PCE processing stages). If destination
+           is multicast, there is no swap, but source MAC address is replaced
+           with global configurable value. */
+       bool ingress_da_sa_swap_enable;
+       /** Egress traffic to this CTP port will be redirected to ingress logical
+           port with same sub interface ID as ingress. */
+       bool egress_loopback_enable;
+       /** Destination/Source MAC address of egress traffic is swapped before
+           transmitted. */
+       bool egress_da_sa_swap_enable;
+
+       /** If enabled, ingress traffic is mirrored to the monitoring port.
+           \remarks
+           This should be used exclusive with b_ingress_loopback_enable. */
+       bool ingress_mirror_enable;
+       /** If enabled, egress traffic is mirrored to the monitoring port.
+           \remarks
+           This should be used exclusive with b_egress_loopback_enable. */
+       bool egress_mirror_enable;
+} mxl862xx_ctp_port_config_t;
+
+#pragma scalar_storage_order default
+#pragma pack(pop)
+
+#endif /*_MXL862XX_CTP_H */
diff --git a/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_flow.h b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_flow.h
new file mode 100644 (file)
index 0000000..69fde14
--- /dev/null
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * drivers/net/dsa/host_api/mxl862xx_flow.h - Header file for DSA Driver for MaxLinear Mxl862xx switch chips family
+ *
+ * Copyright (C) 2024 MaxLinear Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#ifndef _MXL862XX_FLOW_H_
+#define _MXL862XX_FLOW_H_
+
+#include "mxl862xx_types.h"
+
+#pragma pack(push, 1)
+#pragma scalar_storage_order little-endian
+
+#define MPCE_RULES_INDEX 0
+#define MPCE_RULES_INDEX_LAST (MPCE_RULES_INDEX + 7)
+#define EAPOL_PCE_RULE_INDEX 8
+#define BPDU_PCE_RULE_INDEX 9
+#define PFC_PCE_RULE_INDEX 10
+
+/** \brief Register access parameter to directly modify internal registers.
+    Used by \ref GSW_REGISTER_MOD. */
+typedef struct {
+       /** Register Address Offset for modifiation. */
+       u16 reg_addr;
+       /** Value to write to 'reg_addr'. */
+       u16 data;
+       /** Mask of bits to be modified. 1 to modify, 0 to ignore. */
+       u16 mask;
+} mxl862xx_register_mod_t;
+
+#pragma scalar_storage_order default
+#pragma pack(pop)
+
+#endif /* _MXL862XX_FLOW_H_ */
diff --git a/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_host_api_impl.c b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_host_api_impl.c
new file mode 100644 (file)
index 0000000..1c91d2c
--- /dev/null
@@ -0,0 +1,514 @@
+// spdx-license-identifier: gpl-2.0
+/*
+ * drivers/net/dsa/host_api/mxl862xx_host_api_impl.c - dsa driver for maxlinear mxl862xx switch chips family
+ *
+ * copyright (c) 2024 maxlinear inc.
+ *
+ * this program is free software; you can redistribute it and/or
+ * modify it under the terms of the gnu general public license
+ * as published by the free software foundation; either version 2
+ * of the license, or (at your option) any later version.
+ *
+ * this program is distributed in the hope that it will be useful,
+ * but without any warranty; without even the implied warranty of
+ * merchantability or fitness for a particular purpose.  see the
+ * gnu general public license for more details.
+ *
+ * you should have received a copy of the gnu general public license
+ * along with this program; if not, write to the free software
+ * foundation, inc., 51 franklin street, fifth floor, boston, ma  02110-1301, usa.
+ *
+ */
+
+#include "mxl862xx_mmd_apis.h"
+
+#define CTRL_BUSY_MASK BIT(15)
+#define CTRL_CMD_MASK (BIT(15) - 1)
+
+#define MAX_BUSY_LOOP 1000 /* roughly 10ms */
+
+#define THR_RST_DATA 5
+
+#define ENABLE_GETSET_OPT 1
+
+//#define C22_MDIO
+
+#if defined(ENABLE_GETSET_OPT) && ENABLE_GETSET_OPT
+static struct {
+       uint16_t ctrl;
+       int16_t ret;
+       mmd_api_data_t data;
+} shadow = { .ctrl = ~0, .ret = -1, .data = { { 0 } } };
+#endif
+
+/* required for Clause 22 extended read/write access */
+#define MXL862XX_MMDDATA                       0xE
+#define MXL862XX_MMDCTRL                       0xD
+
+#define MXL862XX_ACTYPE_ADDRESS                (0 << 14)
+#define MXL862XX_ACTYPE_DATA                   (1 << 14)
+
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#else
+#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0))
+#include <linux/delay.h>
+#endif
+
+#ifdef C22_MDIO
+/**
+ *  write access to MMD register of PHYs via Clause 22 extended access
+ */
+static int __mxl862xx_c22_ext_mmd_write(const mxl862xx_device_t *dev, struct mii_bus *bus, int sw_addr, int mmd,
+                           int reg, u16 val)
+{
+       int res;
+
+       /* Set the DevID for Write Command */
+       res = __mdiobus_write(bus, sw_addr, MXL862XX_MMDCTRL, mmd);
+       if (res < 0)
+               goto error;
+
+       /* Issue the write command */
+       res = __mdiobus_write(bus, sw_addr, MXL862XX_MMDDATA, reg);
+       if (res < 0)
+               goto error;
+
+       /* Set the DevID for Write Command */
+       res = __mdiobus_write(bus, sw_addr, MXL862XX_MMDCTRL, MXL862XX_ACTYPE_DATA | mmd);
+       if (res < 0)
+               goto error;
+
+       /* Issue the write command */
+       res = __mdiobus_write(bus, sw_addr, MXL862XX_MMDDATA, val);
+       if (res < 0)
+               goto error;
+
+error:
+       return res;
+}
+
+/**
+ *  read access to MMD register of PHYs via Clause 22 extended access
+ */
+static int __mxl862xx_c22_ext_mmd_read(const mxl862xx_device_t *dev, struct mii_bus *bus, int sw_addr, int mmd, int reg)
+{
+       int res;
+
+/* Set the DevID for Write Command */
+       res = __mdiobus_write(bus, sw_addr, MXL862XX_MMDCTRL, mmd);
+       if (res < 0)
+               goto error;
+
+       /* Issue the write command */
+       res = __mdiobus_write(bus, sw_addr, MXL862XX_MMDDATA, reg);
+       if (res < 0)
+               goto error;
+
+       /* Set the DevID for Write Command */
+       res = __mdiobus_write(bus, sw_addr, MXL862XX_MMDCTRL, MXL862XX_ACTYPE_DATA | mmd);
+       if (res < 0)
+               goto error;
+
+       /* Read the data */
+       res = __mdiobus_read(bus, sw_addr, MXL862XX_MMDDATA);
+       if (res < 0)
+               goto error;
+
+error:
+       return res;
+}
+#endif
+
+int mxl862xx_read(const mxl862xx_device_t *dev, uint32_t regaddr)
+{
+       int mmd = MXL862XX_MMD_DEV;
+#ifdef C22_MDIO
+       int ret = __mxl862xx_c22_ext_mmd_read(dev, dev->bus, dev->sw_addr, mmd, regaddr);
+#else
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 8, 0))
+       u32 addr = MII_ADDR_C45 | (mmd << 16) | (regaddr & 0xffff);
+       int ret = __mdiobus_read(dev->bus, dev->sw_addr, addr);
+#else
+       int ret = __mdiobus_c45_read(dev->bus, dev->sw_addr, mmd, regaddr);
+#endif
+#endif
+       return ret;
+}
+
+int mxl862xx_write(const mxl862xx_device_t *dev, uint32_t regaddr, uint16_t data)
+{
+       int mmd = MXL862XX_MMD_DEV;
+#ifdef C22_MDIO
+       int ret = __mxl862xx_c22_ext_mmd_write(dev, dev->bus, dev->sw_addr, mmd, regaddr, data);
+#else
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 8, 0))
+       u32 addr = MII_ADDR_C45 | (mmd << 16) | (regaddr & 0xffff);
+       int ret = __mdiobus_write(dev->bus, dev->sw_addr, addr, data);
+#else
+       int ret = __mdiobus_c45_write(dev->bus, dev->sw_addr, mmd, regaddr, data);
+#endif
+#endif
+       return ret;
+}
+
+static int __wait_ctrl_busy(const mxl862xx_device_t *dev)
+{
+       int ret, i;
+
+       for (i = 0; i < MAX_BUSY_LOOP; i++) {
+               ret = mxl862xx_read(dev, MXL862XX_MMD_REG_CTRL);
+               if (ret < 0) {
+                       goto busy_check_exit;
+               }
+
+               if (!(ret & CTRL_BUSY_MASK)) {
+                       ret = 0;
+                       goto busy_check_exit;
+               }
+
+               usleep_range(10, 15);
+       }
+       ret = -ETIMEDOUT;
+busy_check_exit:
+       return ret;
+}
+
+static int __mxl862xx_rst_data(const mxl862xx_device_t *dev)
+{
+       int ret;
+
+       ret = mxl862xx_write(dev, MXL862XX_MMD_REG_LEN_RET, 0);
+       if (ret < 0)
+               return ret;
+
+       ret = mxl862xx_write(dev, MXL862XX_MMD_REG_CTRL,
+                       MMD_API_RST_DATA | CTRL_BUSY_MASK);
+       if (ret < 0)
+               return ret;
+
+       return __wait_ctrl_busy(dev);
+}
+
+static int __mxl862xx_set_data(const mxl862xx_device_t *dev, uint16_t words)
+{
+       int ret;
+       uint16_t cmd;
+
+       ret = mxl862xx_write(dev, MXL862XX_MMD_REG_LEN_RET,
+                       MXL862XX_MMD_REG_DATA_MAX_SIZE * sizeof(uint16_t));
+       if (ret < 0)
+               return ret;
+
+       cmd = words / MXL862XX_MMD_REG_DATA_MAX_SIZE - 1;
+       if (!(cmd < 2))
+               return -EINVAL;
+
+       cmd += MMD_API_SET_DATA_0;
+       ret = mxl862xx_write(dev, MXL862XX_MMD_REG_CTRL, cmd | CTRL_BUSY_MASK);
+       if (ret < 0)
+               return ret;
+
+       return __wait_ctrl_busy(dev);
+}
+
+static int __mxl862xx_get_data(const mxl862xx_device_t *dev, uint16_t words)
+{
+       int ret;
+       uint16_t cmd;
+
+       ret = mxl862xx_write(dev, MXL862XX_MMD_REG_LEN_RET,
+                       MXL862XX_MMD_REG_DATA_MAX_SIZE * sizeof(uint16_t));
+       if (ret < 0)
+               return ret;
+
+       cmd = words / MXL862XX_MMD_REG_DATA_MAX_SIZE;
+       if (!(cmd > 0 && cmd < 3))
+               return -EINVAL;
+       cmd += MMD_API_GET_DATA_0;
+       ret = mxl862xx_write(dev, MXL862XX_MMD_REG_CTRL, cmd | CTRL_BUSY_MASK);
+       if (ret < 0)
+               return ret;
+
+       return __wait_ctrl_busy(dev);
+}
+
+static int __mxl862xx_send_cmd(const mxl862xx_device_t *dev, uint16_t cmd, uint16_t size,
+                         int16_t *presult)
+{
+       int ret;
+
+       ret = mxl862xx_write(dev, MXL862XX_MMD_REG_LEN_RET, size);
+       if (ret < 0) {
+               return ret;
+       }
+
+       ret = mxl862xx_write(dev, MXL862XX_MMD_REG_CTRL, cmd | CTRL_BUSY_MASK);
+       if (ret < 0) {
+               return ret;
+       }
+
+       ret = __wait_ctrl_busy(dev);
+       if (ret < 0) {
+               return ret;
+       }
+
+       ret = mxl862xx_read(dev, MXL862XX_MMD_REG_LEN_RET);
+       if (ret < 0) {
+               return ret;
+       }
+
+       *presult = ret;
+       return 0;
+}
+
+static bool __mxl862xx_cmd_r_valid(uint16_t cmd_r)
+{
+#if defined(ENABLE_GETSET_OPT) && ENABLE_GETSET_OPT
+       return (shadow.ctrl == cmd_r && shadow.ret >= 0) ? true : false;
+#else
+       return false;
+#endif
+}
+
+/* This is usually used to implement CFG_SET command.
+ * With previous CFG_GET command executed properly, the retrieved data
+ * are shadowed in local structure. WSP FW has a set of shadow too,
+ * so that only the difference to be sent over SMDIO.
+ */
+static int __mxl862xx_api_wrap_cmd_r(const mxl862xx_device_t *dev, uint16_t cmd,
+                               void *pdata, uint16_t size, uint16_t r_size)
+{
+#if defined(ENABLE_GETSET_OPT) && ENABLE_GETSET_OPT
+       int ret;
+
+       uint16_t max, i;
+       uint16_t *data;
+       int16_t result = 0;
+
+       max = (size + 1) / 2;
+       data = pdata;
+
+       ret = __wait_ctrl_busy(dev);
+       if (ret < 0) {
+               return ret;
+       }
+
+       for (i = 0; i < max; i++) {
+               uint16_t off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE;
+
+               if (i && off == 0) {
+                       /* Send command to set data when every
+                        * MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs are written
+                        * and reload next batch of data from last CFG_GET.
+                        */
+                       ret = __mxl862xx_set_data(dev, i);
+                       if (ret < 0) {
+                               return ret;
+                       }
+               }
+
+               if (data[i] == shadow.data.data[i])
+                       continue;
+
+               mxl862xx_write(dev, MXL862XX_MMD_REG_DATA_FIRST + off,
+                         le16_to_cpu(data[i]));
+               //sys_le16_to_cpu(data[i]));
+       }
+
+       ret = __mxl862xx_send_cmd(dev, cmd, size, &result);
+       if (ret < 0) {
+               return ret;
+       }
+
+       if (result < 0) {
+               return result;
+       }
+
+       max = (r_size + 1) / 2;
+       for (i = 0; i < max; i++) {
+               uint16_t off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE;
+
+               if (i && off == 0) {
+                       /* Send command to fetch next batch of data
+                        * when every MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs
+                        * are read.
+                        */
+                       ret = __mxl862xx_get_data(dev, i);
+                       if (ret < 0) {
+                               return ret;
+                       }
+               }
+
+               ret = mxl862xx_read(dev, MXL862XX_MMD_REG_DATA_FIRST + off);
+               if (ret < 0) {
+                       return ret;
+               }
+
+               if ((i * 2 + 1) == r_size) {
+                       /* Special handling for last BYTE
+                        * if it's not WORD aligned.
+                        */
+                       *(uint8_t *)&data[i] = ret & 0xFF;
+               } else {
+                       data[i] = cpu_to_le16((uint16_t)ret);
+               }
+       }
+
+       shadow.data.data[max] = 0;
+       memcpy(shadow.data.data, data, r_size);
+
+       return result;
+#else /* defined(ENABLE_GETSET_OPT) && ENABLE_GETSET_OPT */
+       ARG_UNUSED(dev);
+       ARG_UNUSED(cmd);
+       ARG_UNUSED(pdata);
+       ARG_UNUSED(size);
+       return -ENOTSUP;
+#endif /* defined(ENABLE_GETSET_OPT) && ENABLE_GETSET_OPT */
+}
+
+int mxl862xx_api_wrap(const mxl862xx_device_t *dev, uint16_t cmd, void *pdata,
+                uint16_t size, uint16_t cmd_r, uint16_t r_size)
+{
+       int ret;
+       uint16_t max, i, cnt;
+       uint16_t *data;
+       int16_t result = 0;
+
+       if (!dev || (!pdata && size))
+               return -EINVAL;
+
+       if (!(size <= sizeof(mmd_api_data_t)) || !(r_size <= size))
+               return -EINVAL;
+
+       mutex_lock_nested(&dev->bus->mdio_lock, MDIO_MUTEX_NESTED);
+
+       if (__mxl862xx_cmd_r_valid(cmd_r)) {
+               /* Special handling for GET and SET command pair. */
+               ret = __mxl862xx_api_wrap_cmd_r(dev, cmd, pdata, size, r_size);
+               goto EXIT;
+       }
+
+       max = (size + 1) / 2;
+       data = pdata;
+
+       /* Check whether it's worth to issue RST_DATA command. */
+       for (i = cnt = 0; i < max && cnt < THR_RST_DATA; i++) {
+               if (!data[i])
+                       cnt++;
+       }
+
+       ret = __wait_ctrl_busy(dev);
+       if (ret < 0)
+               goto EXIT;
+
+       if (cnt >= THR_RST_DATA) {
+               /* Issue RST_DATA commdand. */
+               ret = __mxl862xx_rst_data(dev);
+               if (ret < 0)
+                       goto EXIT;
+
+               for (i = 0, cnt = 0; i < max; i++) {
+                       uint16_t off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE;
+
+                       if (i && off == 0) {
+                               uint16_t cnt_old = cnt;
+
+                               cnt = 0;
+
+                               /* No actual data was written. */
+                               if (!cnt_old)
+                                       continue;
+
+                               /* Send command to set data when every
+                                * MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs are written
+                                * and clear the MMD register space.
+                                */
+                               ret = __mxl862xx_set_data(dev, i);
+                               if (ret < 0)
+                                       goto EXIT;
+                       }
+
+                       /* Skip '0' data. */
+                       if (!data[i])
+                               continue;
+
+                       mxl862xx_write(dev, MXL862XX_MMD_REG_DATA_FIRST + off,
+                                 le16_to_cpu(data[i]));
+                       cnt++;
+               }
+       } else {
+               for (i = 0; i < max; i++) {
+                       uint16_t off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE;
+
+                       if (i && off == 0) {
+                               /* Send command to set data when every
+                                * MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs are written.
+                                */
+                               ret = __mxl862xx_set_data(dev, i);
+                               if (ret < 0)
+                                       goto EXIT;
+                       }
+
+                       mxl862xx_write(dev, MXL862XX_MMD_REG_DATA_FIRST + off,
+                                 le16_to_cpu(data[i]));
+               }
+       }
+
+       ret = __mxl862xx_send_cmd(dev, cmd, size, &result);
+       if (ret < 0)
+               goto EXIT;
+
+       if (result < 0) {
+               ret = result;
+               goto EXIT;
+       }
+
+       max = (r_size + 1) / 2;
+       for (i = 0; i < max; i++) {
+               uint16_t off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE;
+
+               if (i && off == 0) {
+                       /* Send command to fetch next batch of data
+                        * when every MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs
+                        * are read.
+                        */
+                       ret = __mxl862xx_get_data(dev, i);
+                       if (ret < 0)
+                               goto EXIT;
+               }
+
+               ret = mxl862xx_read(dev, MXL862XX_MMD_REG_DATA_FIRST + off);
+               if (ret < 0)
+                       goto EXIT;
+
+               if ((i * 2 + 1) == r_size) {
+                       /* Special handling for last BYTE
+                        * if it's not WORD aligned.
+                        */
+                       *(uint8_t *)&data[i] = ret & 0xFF;
+               } else {
+                       data[i] = cpu_to_le16((uint16_t)ret);
+               }
+       }
+
+#if defined(ENABLE_GETSET_OPT) && ENABLE_GETSET_OPT
+       if ((cmd != 0x1801) && (cmd != 0x1802))
+               shadow.data.data[max] = 0;
+       memcpy(shadow.data.data, data, r_size);
+#endif
+
+       ret = result;
+
+EXIT:
+#if defined(ENABLE_GETSET_OPT) && ENABLE_GETSET_OPT
+       shadow.ctrl = cmd;
+       shadow.ret = ret;
+#endif
+       mutex_unlock(&dev->bus->mdio_lock);
+       return ret;
+}
diff --git a/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_host_api_impl.h b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_host_api_impl.h
new file mode 100644 (file)
index 0000000..707700c
--- /dev/null
@@ -0,0 +1,31 @@
+// spdx-license-identifier: gpl-2.0
+/*
+ * drivers/net/dsa/host_api/mxl862xx_host_api_impl.h - dsa driver for maxlinear mxl862xx switch chips family
+ *
+ * copyright (c) 2024 maxlinear inc.
+ *
+ * this program is free software; you can redistribute it and/or
+ * modify it under the terms of the gnu general public license
+ * as published by the free software foundation; either version 2
+ * of the license, or (at your option) any later version.
+ *
+ * this program is distributed in the hope that it will be useful,
+ * but without any warranty; without even the implied warranty of
+ * merchantability or fitness for a particular purpose.  see the
+ * gnu general public license for more details.
+ *
+ * you should have received a copy of the gnu general public license
+ * along with this program; if not, write to the free software
+ * foundation, inc., 51 franklin street, fifth floor, boston, ma  02110-1301, usa.
+ *
+ */
+
+#ifndef _MXL862XX_HOST_API_IMPL_H_
+#define _MXL862XX_HOST_API_IMPL_H_
+
+#include "mxl862xx_types.h"
+
+extern int mxl862xx_api_wrap(const mxl862xx_device_t *dev, uint16_t cmd, void *pdata,
+                       uint16_t size, uint16_t cmd_r, uint16_t r_size);
+
+#endif /* _MXL862XX_HOST_API_IMPL_H_ */
diff --git a/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_mdio_relay.c b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_mdio_relay.c
new file mode 100644 (file)
index 0000000..03702d8
--- /dev/null
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * drivers/net/dsa/host_api/mxl862xx_mdio_relay.c - dsa driver for Maxlinear mxl862xx switch chips family
+ *
+ * Copyright (C) 2024 MaxLinear Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#include "mxl862xx_mdio_relay.h"
+#include "mxl862xx_host_api_impl.h"
+#include "mxl862xx_mmd_apis.h"
+
+int int_gphy_read(const mxl862xx_device_t *dev, struct mdio_relay_data *parm)
+{
+       return mxl862xx_api_wrap(dev, INT_GPHY_READ, parm, sizeof(*parm), 0,
+                           sizeof(parm->data));
+}
+
+int int_gphy_write(const mxl862xx_device_t *dev, struct mdio_relay_data *parm)
+{
+       return mxl862xx_api_wrap(dev, INT_GPHY_WRITE, parm, sizeof(*parm), 0, 0);
+}
+
+int int_gphy_mod(const mxl862xx_device_t *dev, struct mdio_relay_mod_data *parm)
+{
+       return mxl862xx_api_wrap(dev, INT_GPHY_MOD, parm, sizeof(*parm), 0, 0);
+}
+
+int ext_mdio_read(const mxl862xx_device_t *dev, struct mdio_relay_data *parm)
+{
+       return mxl862xx_api_wrap(dev, EXT_MDIO_READ, parm, sizeof(*parm), 0,
+                           sizeof(parm->data));
+}
+
+int ext_mdio_write(const mxl862xx_device_t *dev, struct mdio_relay_data *parm)
+{
+       return mxl862xx_api_wrap(dev, EXT_MDIO_WRITE, parm, sizeof(*parm), 0, 0);
+}
+
+int ext_mdio_mod(const mxl862xx_device_t *dev, struct mdio_relay_mod_data *parm)
+{
+       return mxl862xx_api_wrap(dev, EXT_MDIO_MOD, parm, sizeof(*parm), 0, 0);
+}
+
+EXPORT_SYMBOL(int_gphy_write);
+EXPORT_SYMBOL(int_gphy_read);
+EXPORT_SYMBOL(int_gphy_mod);
diff --git a/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_mdio_relay.h b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_mdio_relay.h
new file mode 100644 (file)
index 0000000..5e46947
--- /dev/null
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * drivers/net/dsa/host_api/mxl862xx_mdio_relay.h - Header file for DSA Driver for MaxLinear Mxl862xx switch chips family
+ *
+ * Copyright (C) 2024 MaxLinear Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#ifndef _MXL862XX_MDIO_RELAY_H_
+#define _MXL862XX_MDIO_RELAY_H_
+
+#include <linux/types.h>
+#include "mxl862xx_types.h"
+
+#pragma pack(push, 1)
+#pragma scalar_storage_order little-endian
+
+struct mdio_relay_data {
+       /* data to be read or written */
+       uint16_t data;
+       /* PHY index (0~7) for internal PHY
+        * PHY address (0~31) for external PHY access via MDIO bus
+        */
+       uint8_t phy;
+       /* MMD device (0~31) */
+       uint8_t mmd;
+       /* Register Index
+        * 0~31 if mmd is 0 (CL22)
+        * 0~65535 otherwise (CL45)
+        */
+       uint16_t reg;
+};
+
+struct mdio_relay_mod_data {
+       /* data to be written with mask */
+       uint16_t data;
+       /* PHY index (0~7) for internal PHY
+        * PHY address (0~31) for external PHY access via MDIO bus
+        */
+       uint8_t phy;
+       /* MMD device (0~31) */
+       uint8_t mmd;
+       /* Register Index
+        * 0~31 if mmd is 0 (CL22)
+        * 0~65535 otherwise (CL45)
+        */
+       uint16_t reg;
+       /* mask of bit fields to be updated
+        * 1 to write the bit
+        * 0 to ignore
+        */
+       uint16_t mask;
+};
+
+#pragma scalar_storage_order default
+#pragma pack(pop)
+
+/* read internal GPHY MDIO/MMD registers */
+int int_gphy_read(const mxl862xx_device_t *dev, struct mdio_relay_data *pdata);
+/* write internal GPHY MDIO/MMD registers */
+int int_gphy_write(const mxl862xx_device_t *dev, struct mdio_relay_data *pdata);
+/* modify internal GPHY MDIO/MMD registers */
+int int_gphy_mod(const mxl862xx_device_t *dev, struct mdio_relay_mod_data *pdata);
+
+/* read external GPHY MDIO/MMD registers via MDIO bus */
+int ext_mdio_read(const mxl862xx_device_t *dev, struct mdio_relay_data *pdata);
+/* write external GPHY MDIO/MMD registers via MDIO bus */
+int ext_mdio_write(const mxl862xx_device_t *dev, struct mdio_relay_data *pdata);
+/* modify external GPHY MDIO/MMD registers via MDIO bus */
+int ext_mdio_mod(const mxl862xx_device_t *dev, struct mdio_relay_mod_data *pdata);
+
+#endif /*  _MXL862XX_MDIO_RELAY_H_ */
diff --git a/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_mmd_apis.h b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_mmd_apis.h
new file mode 100644 (file)
index 0000000..6f58974
--- /dev/null
@@ -0,0 +1,279 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * drivers/net/dsa/host_api/mxl862xx_mmd_apis.h - Header file for DSA Driver for MaxLinear Mxl862xx switch chips family
+ *
+ * Copyright (C) 2024 MaxLinear Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#ifndef _MXL862XX_MMD_APIS_H_
+#define _MXL862XX_MMD_APIS_H_
+
+#include "mxl862xx_api.h"
+#include "mxl862xx_mdio_relay.h"
+
+#define MXL862XX_MMD_DEV 30
+#define MXL862XX_MMD_REG_CTRL 0
+#define MXL862XX_MMD_REG_LEN_RET 1
+#define MXL862XX_MMD_REG_DATA_FIRST 2
+#define MXL862XX_MMD_REG_DATA_LAST 95
+#define MXL862XX_MMD_REG_DATA_MAX_SIZE \
+       (MXL862XX_MMD_REG_DATA_LAST - MXL862XX_MMD_REG_DATA_FIRST + 1)
+
+typedef union mmd_api_data {
+       uint16_t data[MXL862XX_MMD_REG_DATA_MAX_SIZE * 3]; //Maximum data size is GSW_PCE_rule_t (508)
+       mxl862xx_register_mod_t mxl862xx_register_mod_t_data;
+       mxl862xx_cpu_port_cfg_t mxl862xx_cpu_port_cfg_t_data;
+       mxl862xx_port_link_cfg_t mxl862xx_port_link_cfg_t_data;
+       mxl862xx_port_cfg_t mxl862xx_port_cfg_t_data;
+       mxl862xx_bridge_alloc_t mxl862xx_bridge_alloc_t_data;
+       mxl862xx_bridge_port_config_t mxl862xx_bridge_port_config_t_data;
+       mxl862xx_debug_rmon_port_cnt_t mxl862xx_debug_rmon_port_cnt_t_data;
+       mxl862xx_mac_table_clear_cond_t mxl862xx_mac_table_clear_cond_t_data;
+       mxl862xx_mac_table_read_t mxl862xx_mac_table_read_t_data;
+       mxl862xx_mac_table_add_t mxl862xx_mac_table_add_t_data;
+       mxl862xx_mac_table_remove_t mxl862xx_mac_table_remove_t_data;
+       mxl862xx_stp_port_cfg_t mxl862xx_stp_port_cfg_t_data;
+       mxl862xx_ss_sp_tag_t mxl862xx_ss_sp_tag_t_data;
+       mxl862xx_monitor_port_cfg_t mxl862xx_monitor_port_cfg_t_data;
+       mxl862xx_ctp_port_config_t mxl862xx_ctp_port_config_t_data;
+
+       struct mdio_relay_data mdio_relay_data;
+       struct mdio_relay_mod_data mdio_relay_mod_data;
+       struct sys_fw_image_version img_ver_data;
+#ifdef CONFIG_SENSOR_MXL
+       struct sys_sensor_value pvt_sensor_data;
+#endif
+} mmd_api_data_t;
+
+#define MXL862XX_COMMON_MAGIC 0x100
+#define MXL862XX_TFLOW_MAGIC 0x200
+#define MXL862XX_BRDG_MAGIC 0x300
+#define MXL862XX_BRDGPORT_MAGIC 0x400
+#define MXL862XX_CTP_MAGIC 0x500
+#define MXL862XX_QOS_MAGIC 0x600
+#define MXL862XX_RMON_MAGIC 0x700
+#define MXL862XX_DEBUG_MAGIC 0x800
+#define MXL862XX_PMAC_MAGIC 0x900
+#define MXL862XX_SWMAC_MAGIC 0xA00
+#define MXL862XX_EXTVLAN_MAGIC 0xB00
+#define MXL862XX_VLANFILTER_MAGIC 0xC00
+#define MXL862XX_MULTICAST_MAGIC 0xD00
+#define MXL862XX_TRUNKING_MAGIC 0xE00
+#define MXL862XX_STP_MAGIC 0xF00
+#define MXL862XX_PBB_MAGIC 0x1000
+#define MXL862XX_VLAN_RMON_MAGIC 0x1100
+#define MXL862XX_SS_MAGIC 0x1600
+
+#define GPY_GPY2XX_MAGIC 0x1800
+
+#define SYS_MISC_MAGIC 0x1900
+
+#ifdef MMD_API_TEST
+#define MMD_API_SIMPLE_TEST (0x0 + 0x1)
+#endif
+#define MMD_API_SET_DATA_0 (0x0 + 0x2)
+#define MMD_API_SET_DATA_1 (0x0 + 0x3)
+#define MMD_API_SET_DATA_2 (0x0 + 0x4)
+#define MMD_API_GET_DATA_0 (0x0 + 0x5)
+#define MMD_API_GET_DATA_1 (0x0 + 0x6)
+#define MMD_API_GET_DATA_2 (0x0 + 0x7)
+#define MMD_API_RST_DATA (0x0 + 0x8)
+
+#define MXL862XX_COMMON_REGISTERGET (MXL862XX_COMMON_MAGIC + 0x1)
+#define MXL862XX_COMMON_REGISTERSET (MXL862XX_COMMON_MAGIC + 0x2)
+#define MXL862XX_COMMON_CPU_PORTCFGGET (MXL862XX_COMMON_MAGIC + 0x3)
+#define MXL862XX_COMMON_CPU_PORTCFGSET (MXL862XX_COMMON_MAGIC + 0x4)
+#define MXL862XX_COMMON_PORTLINKCFGGET (MXL862XX_COMMON_MAGIC + 0x5)
+#define MXL862XX_COMMON_PORTLINKCFGSET (MXL862XX_COMMON_MAGIC + 0x6)
+#define MXL862XX_COMMON_PORTCFGGET (MXL862XX_COMMON_MAGIC + 0x7)
+#define MXL862XX_COMMON_PORTCFGSET (MXL862XX_COMMON_MAGIC + 0x8)
+#define MXL862XX_COMMON_CFGGET (MXL862XX_COMMON_MAGIC + 0x9)
+#define MXL862XX_COMMON_CFGSET (MXL862XX_COMMON_MAGIC + 0xA)
+#define MXL862XX_COMMON_MONITORPORTCFGGET (MXL862XX_COMMON_MAGIC + 0xD)
+#define MXL862XX_COMMON_MONITORPORTCFGSET (MXL862XX_COMMON_MAGIC + 0xE)
+#define MXL862XX_COMMON_FREEZE (MXL862XX_COMMON_MAGIC + 0xF)
+#define MXL862XX_COMMON_UNFREEZE (MXL862XX_COMMON_MAGIC + 0x10)
+#define MXL862XX_COMMON_REGISTERMOD (MXL862XX_COMMON_MAGIC + 0x11)
+
+#define MXL862XX_TFLOW_PCERULEREAD (MXL862XX_TFLOW_MAGIC + 0x1)
+#define MXL862XX_TFLOW_PCERULEWRITE (MXL862XX_TFLOW_MAGIC + 0x2)
+#define MXL862XX_TFLOW_PCERULEDELETE (MXL862XX_TFLOW_MAGIC + 0x3)
+#define MXL862XX_TFLOW_PCERULEALLOC (MXL862XX_TFLOW_MAGIC + 0x4)
+#define MXL862XX_TFLOW_PCERULEFREE (MXL862XX_TFLOW_MAGIC + 0x5)
+#define MXL862XX_TFLOW_PCERULEENABLE (MXL862XX_TFLOW_MAGIC + 0x6)
+#define MXL862XX_TFLOW_PCERULEDISABLE (MXL862XX_TFLOW_MAGIC + 0x7)
+
+#define MXL862XX_BRIDGE_ALLOC (MXL862XX_BRDG_MAGIC + 0x1)
+#define MXL862XX_BRIDGE_CONFIGSET (MXL862XX_BRDG_MAGIC + 0x2)
+#define MXL862XX_BRIDGE_CONFIGGET (MXL862XX_BRDG_MAGIC + 0x3)
+#define MXL862XX_BRIDGE_FREE (MXL862XX_BRDG_MAGIC + 0x4)
+
+#define MXL862XX_BRIDGEPORT_ALLOC (MXL862XX_BRDGPORT_MAGIC + 0x1)
+#define MXL862XX_BRIDGEPORT_CONFIGSET (MXL862XX_BRDGPORT_MAGIC + 0x2)
+#define MXL862XX_BRIDGEPORT_CONFIGGET (MXL862XX_BRDGPORT_MAGIC + 0x3)
+#define MXL862XX_BRIDGEPORT_FREE (MXL862XX_BRDGPORT_MAGIC + 0x4)
+
+#define MXL862XX_CTP_PORTASSIGNMENTALLOC (MXL862XX_CTP_MAGIC + 0x1)
+#define MXL862XX_CTP_PORTASSIGNMENTFREE (MXL862XX_CTP_MAGIC + 0x2)
+#define MXL862XX_CTP_PORTASSIGNMENTSET (MXL862XX_CTP_MAGIC + 0x3)
+#define MXL862XX_CTP_PORTASSIGNMENTGET (MXL862XX_CTP_MAGIC + 0x4)
+#define MXL862XX_CTP_PORTCONFIGSET (MXL862XX_CTP_MAGIC + 0x5)
+#define MXL862XX_CTP_PORTCONFIGGET (MXL862XX_CTP_MAGIC + 0x6)
+#define MXL862XX_CTP_PORTCONFIGRESET (MXL862XX_CTP_MAGIC + 0x7)
+
+#define MXL862XX_QOS_METERCFGGET (MXL862XX_QOS_MAGIC + 0x1)
+#define MXL862XX_QOS_METERCFGSET (MXL862XX_QOS_MAGIC + 0x2)
+#define MXL862XX_QOS_DSCP_CLASSGET (MXL862XX_QOS_MAGIC + 0x4)
+#define MXL862XX_QOS_DSCP_CLASSSET (MXL862XX_QOS_MAGIC + 0x5)
+#define MXL862XX_QOS_DSCP_DROPPRECEDENCECFGGET (MXL862XX_QOS_MAGIC + 0x6)
+#define MXL862XX_QOS_DSCP_DROPPRECEDENCECFGSET (MXL862XX_QOS_MAGIC + 0x7)
+#define MXL862XX_QOS_PORTREMARKINGCFGGET (MXL862XX_QOS_MAGIC + 0x8)
+#define MXL862XX_QOS_PORTREMARKINGCFGSET (MXL862XX_QOS_MAGIC + 0x9)
+#define MXL862XX_QOS_PCP_CLASSGET (MXL862XX_QOS_MAGIC + 0xA)
+#define MXL862XX_QOS_PCP_CLASSSET (MXL862XX_QOS_MAGIC + 0xB)
+#define MXL862XX_QOS_PORTCFGGET (MXL862XX_QOS_MAGIC + 0xC)
+#define MXL862XX_QOS_PORTCFGSET (MXL862XX_QOS_MAGIC + 0xD)
+#define MXL862XX_QOS_QUEUEPORTGET (MXL862XX_QOS_MAGIC + 0xE)
+#define MXL862XX_QOS_QUEUEPORTSET (MXL862XX_QOS_MAGIC + 0xF)
+#define MXL862XX_QOS_SCHEDULERCFGGET (MXL862XX_QOS_MAGIC + 0x10)
+#define MXL862XX_QOS_SCHEDULERCFGSET (MXL862XX_QOS_MAGIC + 0x11)
+#define MXL862XX_QOS_SHAPERCFGGET (MXL862XX_QOS_MAGIC + 0x12)
+#define MXL862XX_QOS_SHAPERCFGSET (MXL862XX_QOS_MAGIC + 0x13)
+#define MXL862XX_QOS_SHAPERQUEUEASSIGN (MXL862XX_QOS_MAGIC + 0x14)
+#define MXL862XX_QOS_SHAPERQUEUEDEASSIGN (MXL862XX_QOS_MAGIC + 0x15)
+#define MXL862XX_QOS_SHAPERQUEUEGET (MXL862XX_QOS_MAGIC + 0x16)
+#define MXL862XX_QOS_STORMCFGSET (MXL862XX_QOS_MAGIC + 0x17)
+#define MXL862XX_QOS_STORMCFGGET (MXL862XX_QOS_MAGIC + 0x18)
+#define MXL862XX_QOS_WREDCFGGET (MXL862XX_QOS_MAGIC + 0x19)
+#define MXL862XX_QOS_WREDCFGSET (MXL862XX_QOS_MAGIC + 0x1A)
+#define MXL862XX_QOS_WREDQUEUECFGGET (MXL862XX_QOS_MAGIC + 0x1B)
+#define MXL862XX_QOS_WREDQUEUECFGSET (MXL862XX_QOS_MAGIC + 0x1C)
+#define MXL862XX_QOS_WREDPORTCFGGET (MXL862XX_QOS_MAGIC + 0x1D)
+#define MXL862XX_QOS_WREDPORTCFGSET (MXL862XX_QOS_MAGIC + 0x1E)
+#define MXL862XX_QOS_FLOWCTRLCFGGET (MXL862XX_QOS_MAGIC + 0x1F)
+#define MXL862XX_QOS_FLOWCTRLCFGSET (MXL862XX_QOS_MAGIC + 0x20)
+#define MXL862XX_QOS_FLOWCTRLPORTCFGGET (MXL862XX_QOS_MAGIC + 0x21)
+#define MXL862XX_QOS_FLOWCTRLPORTCFGSET (MXL862XX_QOS_MAGIC + 0x22)
+#define MXL862XX_QOS_QUEUEBUFFERRESERVECFGGET (MXL862XX_QOS_MAGIC + 0x23)
+#define MXL862XX_QOS_QUEUEBUFFERRESERVECFGSET (MXL862XX_QOS_MAGIC + 0x24)
+#define MXL862XX_QOS_COLORMARKINGTABLEGET (MXL862XX_QOS_MAGIC + 0x26)
+#define MXL862XX_QOS_COLORMARKINGTABLESET (MXL862XX_QOS_MAGIC + 0x27)
+#define MXL862XX_QOS_COLORREMARKINGTABLESET (MXL862XX_QOS_MAGIC + 0x28)
+#define MXL862XX_QOS_COLORREMARKINGTABLEGET (MXL862XX_QOS_MAGIC + 0x29)
+#define MXL862XX_QOS_METERALLOC (MXL862XX_QOS_MAGIC + 0x2A)
+#define MXL862XX_QOS_METERFREE (MXL862XX_QOS_MAGIC + 0x2B)
+#define MXL862XX_QOS_DSCP2PCPTABLESET (MXL862XX_QOS_MAGIC + 0x2C)
+#define MXL862XX_QOS_DSCP2PCPTABLEGET (MXL862XX_QOS_MAGIC + 0x2D)
+#define MXL862XX_QOS_PMAPPERTABLESET (MXL862XX_QOS_MAGIC + 0x2E)
+#define MXL862XX_QOS_PMAPPERTABLEGET (MXL862XX_QOS_MAGIC + 0x2F)
+#define MXL862XX_QOS_SVLAN_PCP_CLASSGET (MXL862XX_QOS_MAGIC + 0x30)
+#define MXL862XX_QOS_SVLAN_PCP_CLASSSET (MXL862XX_QOS_MAGIC + 0x31)
+
+#define MXL862XX_RMON_PORT_GET (MXL862XX_RMON_MAGIC + 0x1)
+#define MXL862XX_RMON_MODE_SET (MXL862XX_RMON_MAGIC + 0x2)
+#define MXL862XX_RMON_METER_GET (MXL862XX_RMON_MAGIC + 0x3)
+#define MXL862XX_RMON_CLEAR (MXL862XX_RMON_MAGIC + 0x4)
+#define MXL862XX_RMON_TFLOWGET (MXL862XX_RMON_MAGIC + 0x5)
+#define MXL862XX_RMON_TFLOWCLEAR (MXL862XX_RMON_MAGIC + 0x6)
+#define MXL862XX_RMON_TFLOWCOUNTMODESET (MXL862XX_RMON_MAGIC + 0x7)
+#define MXL862XX_RMON_TFLOWCOUNTMODEGET (MXL862XX_RMON_MAGIC + 0x8)
+
+#define MXL862XX_DEBUG_RMON_PORT_GET (MXL862XX_DEBUG_MAGIC + 0x1)
+
+#define MXL862XX_PMAC_COUNTGET (MXL862XX_PMAC_MAGIC + 0x1)
+#define MXL862XX_PMAC_GBL_CFGSET (MXL862XX_PMAC_MAGIC + 0x2)
+#define MXL862XX_PMAC_GBL_CFGGET (MXL862XX_PMAC_MAGIC + 0x3)
+#define MXL862XX_PMAC_BM_CFGSET (MXL862XX_PMAC_MAGIC + 0x4)
+#define MXL862XX_PMAC_BM_CFGGET (MXL862XX_PMAC_MAGIC + 0x5)
+#define MXL862XX_PMAC_IG_CFGSET (MXL862XX_PMAC_MAGIC + 0x6)
+#define MXL862XX_PMAC_IG_CFGGET (MXL862XX_PMAC_MAGIC + 0x7)
+#define MXL862XX_PMAC_EG_CFGSET (MXL862XX_PMAC_MAGIC + 0x8)
+#define MXL862XX_PMAC_EG_CFGGET (MXL862XX_PMAC_MAGIC + 0x9)
+
+#define MXL862XX_MAC_TABLECLEAR (MXL862XX_SWMAC_MAGIC + 0x1)
+#define MXL862XX_MAC_TABLEENTRYADD (MXL862XX_SWMAC_MAGIC + 0x2)
+#define MXL862XX_MAC_TABLEENTRYREAD (MXL862XX_SWMAC_MAGIC + 0x3)
+#define MXL862XX_MAC_TABLEENTRYQUERY (MXL862XX_SWMAC_MAGIC + 0x4)
+#define MXL862XX_MAC_TABLEENTRYREMOVE (MXL862XX_SWMAC_MAGIC + 0x5)
+#define MXL862XX_MAC_DEFAULTFILTERSET (MXL862XX_SWMAC_MAGIC + 0x6)
+#define MXL862XX_MAC_DEFAULTFILTERGET (MXL862XX_SWMAC_MAGIC + 0x7)
+#define MXL862XX_MAC_TABLECLEARCOND (MXL862XX_SWMAC_MAGIC + 0x8)
+
+#define MXL862XX_EXTENDEDVLAN_ALLOC (MXL862XX_EXTVLAN_MAGIC + 0x1)
+#define MXL862XX_EXTENDEDVLAN_SET (MXL862XX_EXTVLAN_MAGIC + 0x2)
+#define MXL862XX_EXTENDEDVLAN_GET (MXL862XX_EXTVLAN_MAGIC + 0x3)
+#define MXL862XX_EXTENDEDVLAN_FREE (MXL862XX_EXTVLAN_MAGIC + 0x4)
+
+#define MXL862XX_VLANFILTER_ALLOC (MXL862XX_VLANFILTER_MAGIC + 0x1)
+#define MXL862XX_VLANFILTER_SET (MXL862XX_VLANFILTER_MAGIC + 0x2)
+#define MXL862XX_VLANFILTER_GET (MXL862XX_VLANFILTER_MAGIC + 0x3)
+#define MXL862XX_VLANFILTER_FREE (MXL862XX_VLANFILTER_MAGIC + 0x4)
+
+#define MXL862XX_VLAN_COUNTER_MAPPING_SET (MXL862XX_VLAN_RMON_MAGIC + 0x1)
+#define MXL862XX_VLAN_COUNTER_MAPPING_GET (MXL862XX_VLAN_RMON_MAGIC + 0x2)
+#define MXL862XX_VLAN_RMON_GET (MXL862XX_VLAN_RMON_MAGIC + 0x3)
+#define MXL862XX_VLAN_RMON_CLEAR (MXL862XX_VLAN_RMON_MAGIC + 0x4)
+#define MXL862XX_VLAN_RMON_CONTROL_SET (MXL862XX_VLAN_RMON_MAGIC + 0x5)
+#define MXL862XX_VLAN_RMON_CONTROL_GET (MXL862XX_VLAN_RMON_MAGIC + 0x6)
+
+#define MXL862XX_MULTICAST_ROUTERPORTADD (MXL862XX_MULTICAST_MAGIC + 0x1)
+#define MXL862XX_MULTICAST_ROUTERPORTREAD (MXL862XX_MULTICAST_MAGIC + 0x2)
+#define MXL862XX_MULTICAST_ROUTERPORTREMOVE (MXL862XX_MULTICAST_MAGIC + 0x3)
+#define MXL862XX_MULTICAST_SNOOPCFGGET (MXL862XX_MULTICAST_MAGIC + 0x4)
+#define MXL862XX_MULTICAST_SNOOPCFGSET (MXL862XX_MULTICAST_MAGIC + 0x5)
+#define MXL862XX_MULTICAST_TABLEENTRYADD (MXL862XX_MULTICAST_MAGIC + 0x6)
+#define MXL862XX_MULTICAST_TABLEENTRYREAD (MXL862XX_MULTICAST_MAGIC + 0x7)
+#define MXL862XX_MULTICAST_TABLEENTRYREMOVE (MXL862XX_MULTICAST_MAGIC + 0x8)
+
+#define MXL862XX_TRUNKING_CFGGET (MXL862XX_TRUNKING_MAGIC + 0x1)
+#define MXL862XX_TRUNKING_CFGSET (MXL862XX_TRUNKING_MAGIC + 0x2)
+#define MXL862XX_TRUNKING_PORTCFGGET (MXL862XX_TRUNKING_MAGIC + 0x3)
+#define MXL862XX_TRUNKING_PORTCFGSET (MXL862XX_TRUNKING_MAGIC + 0x4)
+
+#define MXL862XX_STP_PORTCFGGET (MXL862XX_STP_MAGIC + 0x1)
+#define MXL862XX_STP_PORTCFGSET (MXL862XX_STP_MAGIC + 0x2)
+#define MXL862XX_STP_BPDU_RULEGET (MXL862XX_STP_MAGIC + 0x3)
+#define MXL862XX_STP_BPDU_RULESET (MXL862XX_STP_MAGIC + 0x4)
+
+#define MXL862XX_PBB_TEMPLATEALLOC (MXL862XX_PBB_MAGIC + 0x1)
+#define MXL862XX_PBB_TEMPLATEFREE (MXL862XX_PBB_MAGIC + 0x2)
+#define MXL862XX_PBB_TEMPLATESET (MXL862XX_PBB_MAGIC + 0x3)
+#define MXL862XX_PBB_TEMPLATEGET (MXL862XX_PBB_MAGIC + 0x4)
+
+#define MXL862XX_SS_SPTAG_GET (MXL862XX_SS_MAGIC + 0x01)
+#define MXL862XX_SS_SPTAG_SET (MXL862XX_SS_MAGIC + 0x02)
+
+#define INT_GPHY_READ (GPY_GPY2XX_MAGIC + 0x01)
+#define INT_GPHY_WRITE (GPY_GPY2XX_MAGIC + 0x02)
+#define INT_GPHY_MOD (GPY_GPY2XX_MAGIC + 0x03)
+#define EXT_MDIO_READ (GPY_GPY2XX_MAGIC + 0x11)
+#define EXT_MDIO_WRITE (GPY_GPY2XX_MAGIC + 0x12)
+#define EXT_MDIO_MOD (GPY_GPY2XX_MAGIC + 0x13)
+
+#define SYS_MISC_FW_UPDATE (SYS_MISC_MAGIC + 0x01)
+#define SYS_MISC_FW_VERSION (SYS_MISC_MAGIC + 0x02)
+#define SYS_MISC_PVT_TEMP (SYS_MISC_MAGIC + 0x03)
+#define SYS_MISC_PVT_VOLTAGE (SYS_MISC_MAGIC + 0x04)
+
+#define MMD_API_MAXIMUM_ID 0x7FFF
+
+int mxl862xx_read(const mxl862xx_device_t *dev, uint32_t regaddr);
+int mxl862xx_write(const mxl862xx_device_t *dev, uint32_t regaddr, uint16_t data);
+
+
+#endif /* _MXL862XX_MMD_APIS_H_ */
diff --git a/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_rmon.h b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_rmon.h
new file mode 100644 (file)
index 0000000..c4d3a63
--- /dev/null
@@ -0,0 +1,509 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * drivers/net/dsa/host_api/mxl862xx_rmon.h - dsa driver for Maxlinear mxl862xx switch chips family
+ *
+ * Copyright (C) 2024 MaxLinear Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#ifndef _MXL862XX_RMON_H_
+#define _MXL862XX_RMON_H_
+
+#include "mxl862xx_types.h"
+
+#pragma pack(push, 1)
+#pragma scalar_storage_order little-endian
+
+/** \brief Port Type - GSWIP-3.1 only.
+   Used by \ref MXL862XX_port_cfg_t. */
+typedef enum {
+       /** Logical Port */
+       MXL862XX_LOGICAL_PORT = 0,
+       /** Physical Port
+           Applicable only for GSWIP-3.1/3.2 */
+       MXL862XX_PHYSICAL_PORT = 1,
+       /** Connectivity Termination Port (CTP)
+           Applicable only for GSWIP-3.1/3.2 */
+       MXL862XX_CTP_PORT = 2,
+       /** Bridge Port
+           Applicable only for GSWIP-3.1/3.2 */
+       MXL862XX_BRIDGE_PORT = 3
+} mxl862xx_port_type_t;
+
+/**Defined as per RMON counter table structure
+  Applicable only for GSWIP 3.1*/
+typedef enum {
+       MXL862XX_RMON_CTP_PORT_RX = 0,
+       MXL862XX_RMON_CTP_PORT_TX = 1,
+       MXL862XX_RMON_BRIDGE_PORT_RX = 2,
+       MXL862XX_RMON_BRIDGE_PORT_TX = 3,
+       MXL862XX_RMON_CTP_PORT_PCE_BYPASS = 4,
+       MXL862XX_RMON_TFLOW_RX = 5,
+       MXL862XX_RMON_TFLOW_TX = 6,
+       MXL862XX_RMON_QMAP = 0x0E,
+       MXL862XX_RMON_METER = 0x19,
+       MXL862XX_RMON_PMAC = 0x1C,
+} mxl862xx_rmon_port_type_t;
+
+/** Defined as per RMON counter table structure
+ * Applicable only for GSWIP 3.0
+ */
+typedef enum {
+       MXL862XX_RMON_REDIRECTION = 0X18,
+       MXL862XX_RMON_IF = 0x1A,
+       MXL862XX_RMON_ROUTE = 0x1B,
+       MXL862XX_RMON_PMACIG = 0x1C,
+} mxl862xx_rmon_port_t;
+
+typedef enum {
+       /** Parser microcode table */
+       PMAC_BPMAP_INDEX = 0x00,
+       PMAC_IGCFG_INDEX = 0x01,
+       PMAC_EGCFG_INDEX = 0x02,
+} pm_tbl_cmds_t;
+/** \brief RMON Counters Type enumeration.
+    Used by \ref MXL862XX_RMON_clear_t and \ref MXL862XX_RMON_mode_t. */
+typedef enum {
+       /** All RMON Types Counters */
+       MXL862XX_RMON_ALL_TYPE = 0,
+       /** All PMAC RMON Counters */
+       MXL862XX_RMON_PMAC_TYPE = 1,
+       /** Port based RMON Counters */
+       MXL862XX_RMON_PORT_TYPE = 2,
+       /** Meter based RMON Counters */
+       MXL862XX_RMON_METER_TYPE = 3,
+       /** Interface based RMON Counters */
+       MXL862XX_RMON_IF_TYPE = 4,
+       /** Route based RMON Counters */
+       MXL862XX_RMON_ROUTE_TYPE = 5,
+       /** Redirected Traffic based RMON Counters */
+       MXL862XX_RMON_REDIRECT_TYPE = 6,
+       /** Bridge Port based RMON Counters */
+       MXL862XX_RMON_BRIDGE_TYPE = 7,
+       /** CTP Port based RMON Counters */
+       MXL862XX_RMON_CTP_TYPE = 8,
+} mxl862xx_rmon_type_t;
+
+/** \brief RMON Counters Mode Enumeration.
+    This enumeration defines Counters mode - Packets based or Bytes based counting.
+    Metering and Routing Sessions RMON counting support either Byte based or packets based only. */
+typedef enum {
+       /** Packet based RMON Counters */
+       MXL862XX_RMON_COUNT_PKTS = 0,
+       /** Bytes based RMON Counters */
+       MXL862XX_RMON_COUNT_BYTES = 1,
+       /**  number of dropped frames, supported only for interface cunters */
+       MXL862XX_RMON_DROP_COUNT = 2,
+} mxl862xx_rmon_count_mode_t;
+
+/** \brief Used for getting metering RMON counters.
+    Used by \ref MXL862XX_RMON_METER_GET */
+typedef enum {
+       /* Resereved */
+       MXL862XX_RMON_METER_COLOR_RES = 0,
+       /* Green color */
+       MXL862XX_RMON_METER_COLOR_GREEN = 1,
+       /* Yellow color */
+       MXL862XX_RMON_METER_COLOR_YELLOW = 2,
+       /* Red color */
+       MXL862XX_RMON_METER_COLOR_RED = 3,
+} mxl862xx_rmon_meter_color_t;
+
+/* TFLOW counter type */
+typedef enum {
+       /* Set all Rx/Tx/PCE-Bp-Tx registers to same value */
+       MXL862XX_TFLOW_COUNTER_ALL = 0, //Default for 'set' function.
+       /* SEt PCE Rx register config only */
+       MXL862XX_TFLOW_COUNTER_PCE_Rx = 1, //Default for 'get' function.
+       /* SEt PCE Tx register config only */
+       MXL862XX_TFLOW_COUNTER_PCE_Tx = 2,
+       /* SEt PCE-Bypass Tx register config only */
+       MXL862XX_TFLOW_COUNTER_PCE_BP_Tx = 3,
+} mxl862xx_tflow_count_conf_type_t;
+
+/* TFLOW counter mode type */
+typedef enum {
+       /* Global mode */
+       MXL862XX_TFLOW_CMODE_GLOBAL = 0,
+       /* Logical mode */
+       MXL862XX_TFLOW_CMODE_LOGICAL = 1,
+       /* CTP port mode */
+       MXL862XX_TFLOW_CMODE_CTP = 2,
+       /* Bridge port mode */
+       MXL862XX_TFLOW_CMODE_BRIDGE = 3,
+} mxl862xx_tflow_cmode_type_t;
+
+/* TFLOW CTP counter LSB bits */
+typedef enum {
+       /* Num of valid bits  */
+       MXL862XX_TCM_CTP_VAL_BITS_0 = 0,
+       MXL862XX_TCM_CTP_VAL_BITS_1 = 1,
+       MXL862XX_TCM_CTP_VAL_BITS_2 = 2,
+       MXL862XX_TCM_CTP_VAL_BITS_3 = 3,
+       MXL862XX_TCM_CTP_VAL_BITS_4 = 4,
+       MXL862XX_TCM_CTP_VAL_BITS_5 = 5,
+       MXL862XX_TCM_CTP_VAL_BITS_6 = 6,
+} mxl862xx_tflow_ctp_val_bits_t;
+
+/* TFLOW bridge port counter LSB bits */
+typedef enum {
+       /* Num of valid bits  */
+       MXL862XX_TCM_BRP_VAL_BITS_2 = 2,
+       MXL862XX_TCM_BRP_VAL_BITS_3 = 3,
+       MXL862XX_TCM_BRP_VAL_BITS_4 = 4,
+       MXL862XX_TCM_BRP_VAL_BITS_5 = 5,
+       MXL862XX_TCM_BRP_VAL_BITS_6 = 6,
+} mxl862xx_tflow_brp_val_bits_t;
+
+/**
+ \brief RMON Counters for individual Port.
+ This structure contains the RMON counters of an Ethernet Switch Port.
+    Used by \ref MXL862XX_RMON_PORT_GET. */
+typedef struct {
+       /** Port Type. This gives information which type of port to get RMON.
+           port_id should be based on this field.
+           This is new in GSWIP-3.1. For GSWIP-2.1/2.2/3.0, this field is always
+           ZERO (MXL862XX_LOGICAL_PORT). */
+       mxl862xx_port_type_t port_type;
+       /** Ethernet Port number (zero-based counting). The valid range is hardware
+           dependent. An error code is delivered if the selected port is not
+           available. This parameter specifies for which MAC port the RMON
+           counter is read. It has to be set by the application before
+           calling \ref MXL862XX_RMON_PORT_GET. */
+       u16 port_id;
+       /** Sub interface ID group. The valid range is hardware/protocol dependent.
+
+           \remarks
+           This field is valid when \ref MXL862XX_RMON_Port_cnt_t::e_port_type is
+           \ref MXL862XX_port_type_t::MXL862XX_CTP_PORT.
+           Sub interface ID group is defined for each of \ref MXL862XX_Logical_port_mode_t.
+           For both \ref MXL862XX_LOGICAL_PORT_8BIT_WLAN and
+           \ref MXL862XX_LOGICAL_PORT_9BIT_WLAN, this field is VAP.
+           For \ref MXL862XX_LOGICAL_PORT_GPON, this field is GEM index.
+           For \ref MXL862XX_LOGICAL_PORT_EPON, this field is stream index.
+           For \ref MXL862XX_LOGICAL_PORT_GINT, this field is LLID.
+           For others, this field is 0. */
+       u16 sub_if_id_group;
+       /** Separate set of CTP Tx counters when PCE is bypassed. GSWIP-3.1 only.*/
+       bool pce_bypass;
+       /*Applicable only for GSWIP 3.1*/
+       /** Discarded at Extended VLAN Operation Packet Count. GSWIP-3.1 only. */
+       u32 rx_extended_vlan_discard_pkts;
+       /** Discarded MTU Exceeded Packet Count. GSWIP-3.1 only. */
+       u32 mtu_exceed_discard_pkts;
+       /** Tx Undersize (<64) Packet Count. GSWIP-3.1 only. */
+       u32 tx_under_size_good_pkts;
+       /** Tx Oversize (>1518) Packet Count. GSWIP-3.1 only. */
+       u32 tx_oversize_good_pkts;
+       /** Receive Packet Count (only packets that are accepted and not discarded). */
+       u32 rx_good_pkts;
+       /** Receive Unicast Packet Count. */
+       u32 rx_unicast_pkts;
+       /** Receive Broadcast Packet Count. */
+       u32 rx_broadcast_pkts;
+       /** Receive Multicast Packet Count. */
+       u32 rx_multicast_pkts;
+       /** Receive FCS Error Packet Count. */
+       u32 rx_fCSError_pkts;
+       /** Receive Undersize Good Packet Count. */
+       u32 rx_under_size_good_pkts;
+       /** Receive Oversize Good Packet Count. */
+       u32 rx_oversize_good_pkts;
+       /** Receive Undersize Error Packet Count. */
+       u32 rx_under_size_error_pkts;
+       /** Receive Good Pause Packet Count. */
+       u32 rx_good_pause_pkts;
+       /** Receive Oversize Error Packet Count. */
+       u32 rx_oversize_error_pkts;
+       /** Receive Align Error Packet Count. */
+       u32 rx_align_error_pkts;
+       /** Filtered Packet Count. */
+       u32 rx_filtered_pkts;
+       /** Receive Size 64 Bytes Packet Count. */
+       u32 rx64Byte_pkts;
+       /** Receive Size 65-127 Bytes Packet Count. */
+       u32 rx127Byte_pkts;
+       /** Receive Size 128-255 Bytes Packet Count. */
+       u32 rx255Byte_pkts;
+       /** Receive Size 256-511 Bytes Packet Count. */
+       u32 rx511Byte_pkts;
+       /** Receive Size 512-1023 Bytes Packet Count. */
+       u32 rx1023Byte_pkts;
+       /** Receive Size 1024-1522 Bytes (or more, if configured) Packet Count. */
+       u32 rx_max_byte_pkts;
+       /** Overall Transmit Good Packets Count. */
+       u32 tx_good_pkts;
+       /** Transmit Unicast Packet Count. */
+       u32 tx_unicast_pkts;
+       /** Transmit Broadcast Packet Count. */
+       u32 tx_broadcast_pkts;
+       /** Transmit Multicast Packet Count. */
+       u32 tx_multicast_pkts;
+       /** Transmit Single Collision Count. */
+       u32 tx_single_coll_count;
+       /** Transmit Multiple Collision Count. */
+       u32 tx_mult_coll_count;
+       /** Transmit Late Collision Count. */
+       u32 tx_late_coll_count;
+       /** Transmit Excessive Collision Count. */
+       u32 tx_excess_coll_count;
+       /** Transmit Collision Count. */
+       u32 tx_coll_count;
+       /** Transmit Pause Packet Count. */
+       u32 tx_pause_count;
+       /** Transmit Size 64 Bytes Packet Count. */
+       u32 tx64Byte_pkts;
+       /** Transmit Size 65-127 Bytes Packet Count. */
+       u32 tx127Byte_pkts;
+       /** Transmit Size 128-255 Bytes Packet Count. */
+       u32 tx255Byte_pkts;
+       /** Transmit Size 256-511 Bytes Packet Count. */
+       u32 tx511Byte_pkts;
+       /** Transmit Size 512-1023 Bytes Packet Count. */
+       u32 tx1023Byte_pkts;
+       /** Transmit Size 1024-1522 Bytes (or more, if configured) Packet Count. */
+       u32 tx_max_byte_pkts;
+       /** Transmit Drop Packet Count. */
+       u32 tx_dropped_pkts;
+       /** Transmit Dropped Packet Count, based on Congestion Management. */
+       u32 tx_acm_dropped_pkts;
+       /** Receive Dropped Packet Count. */
+       u32 rx_dropped_pkts;
+       /** Receive Good Byte Count (64 bit). */
+       u64 rx_good_bytes;
+       /** Receive Bad Byte Count (64 bit). */
+       u64 rx_bad_bytes;
+       /** Transmit Good Byte Count (64 bit). */
+       u64 tx_good_bytes;
+} mxl862xx_rmon_port_cnt_t;
+
+/** \brief RMON Counters Mode for different Elements.
+    This structure takes RMON Counter Element Name and mode config */
+typedef struct {
+       /** RMON Counters Type */
+       mxl862xx_rmon_type_t rmon_type;
+       /** RMON Counters Mode */
+       mxl862xx_rmon_count_mode_t count_mode;
+} mxl862xx_rmon_mode_t;
+
+/**
+ \brief RMON Counters for Meter - Type (GSWIP-3.0 only).
+ This structure contains the RMON counters of one Meter Instance.
+    Used by \ref MXL862XX_RMON_METER_GET. */
+typedef struct {
+       /** Meter Instance number (zero-based counting). The valid range is hardware
+           dependent. An error code is delivered if the selected meter is not
+           available. This parameter specifies for which Meter Id the RMON-1
+           counter is read. It has to be set by the application before
+           calling \ref MXL862XX_RMON_METER_GET. */
+       u8 meter_id;
+       /** Metered Green colored packets or bytes (depending upon mode) count. */
+       u32 green_count;
+       /** Metered Yellow colored packets or bytes (depending upon mode) count. */
+       u32 yellow_count;
+       /** Metered Red colored packets or bytes (depending upon mode) count. */
+       u32 red_count;
+       /** Metered Reserved (Future Use) packets or bytes (depending upon mode) count. */
+       u32 res_count;
+} mxl862xx_rmon_meter_cnt_t;
+
+/** \brief RMON Counters Data Structure for clearance of values.
+    Used by \ref MXL862XX_RMON_CLEAR. */
+typedef struct {
+       /** RMON Counters Type */
+       mxl862xx_rmon_type_t rmon_type;
+       /** RMON Counters Identifier - Meter, Port, If, Route, etc. */
+       u8 rmon_id;
+} mxl862xx_rmon_clear_t;
+
+/**
+   \brief Hardware platform TFLOW counter mode.
+   Supported modes include, Global (default), Logical, CTP, Bridge port mode.
+   The number of counters that can be assigned varies based these mode type.
+    Used by \ref MXL862XX_TFLOW_COUNT_MODE_SET and MXL862XX_TFLOW_COUNT_MODE_GET. */
+typedef struct {
+       //Counter type. PCE Rx/Tx/Bp-Tx.
+       mxl862xx_tflow_count_conf_type_t count_type;
+       //Counter mode. Global/Logical/CTP/br_p.
+       mxl862xx_tflow_cmode_type_t count_mode;
+       //The below params are valid only for CTP/br_p types.
+       //A group of ports matching MS (9-n) bits. n is n_ctp_lsb or n_brp_lsb.
+       u16 port_msb;
+       //Number of valid bits in CTP port counter mode.
+       mxl862xx_tflow_ctp_val_bits_t ctp_lsb;
+       //Number of valid bits in bridge port counter mode.
+       mxl862xx_tflow_brp_val_bits_t brp_lsb;
+} mxl862xx_tflow_cmode_conf_t;
+
+/**
+   \brief Hardware platform extended RMON Counters. GSWIP-3.1 only.
+   This structure contains additional RMON counters. These counters can be
+   used by the packet classification engine and can be freely assigned to
+   dedicated packet rules and flows.
+    Used by \ref MXL862XX_RMON_FLOW_GET. */
+typedef struct {
+       /** If TRUE, use \ref MXL862XX_RMON_flow_get_t::n_index to access the Flow Counter,
+           otherwise, use \ref MXL862XX_TFLOW_COUNT_MODE_GET to determine mode and use
+           \ref MXL862XX_RMON_flow_get_t::port_id and \ref MXL862XX_RMON_flow_get_t::flow_id
+           to calculate index of the Flow Counter. */
+       bool use_index;
+       /** Absolute index of Flow Counter. */
+       u16 index;
+       /** Port ID. This could be Logical Port, CTP or Bridge Port. It depends
+           on the mode set by \ref MXL862XX_TFLOW_COUNT_MODE_SET. */
+       u16 port_id;
+       /** \ref MXL862XX_PCE_action_t::flow_id. The range depends on the mode set
+           by \ref MXL862XX_TFLOW_COUNT_MODE_SET. */
+       u16 flow_id;
+
+       /** Rx Packet Counter */
+       u32 rx_pkts;
+       /** Tx Packet Counter (non-PCE-Bypass) */
+       u32 tx_pkts;
+       /** Tx Packet Counter (PCE-Bypass) */
+       u32 tx_pce_bypass_pkts;
+} mxl862xx_rmon_flow_get_t;
+
+/** \brief For debugging Purpose only.
+    Used for GSWIP 3.1. */
+
+typedef struct {
+       /** Ethernet Port number (zero-based counting). The valid range is hardware
+           dependent. An error code is delivered if the selected port is not
+           available. This parameter specifies for which MAC port the RMON
+           counter is read. It has to be set by the application before
+           calling \ref MXL862XX_RMON_PORT_GET. */
+       u16 port_id;
+       /**Table address selection based on port type
+         Applicable only for GSWIP 3.1
+        \ref MXL862XX_RMON_port_type_t**/
+       mxl862xx_rmon_port_type_t port_type;
+       /*Applicable only for GSWIP 3.1*/
+       u32 rx_extended_vlan_discard_pkts;
+       /*Applicable only for GSWIP 3.1*/
+       u32 mtu_exceed_discard_pkts;
+       /*Applicable only for GSWIP 3.1*/
+       u32 tx_under_size_good_pkts;
+       /*Applicable only for GSWIP 3.1*/
+       u32 tx_oversize_good_pkts;
+       /** Receive Packet Count (only packets that are accepted and not discarded). */
+       u32 rx_good_pkts;
+       /** Receive Unicast Packet Count. */
+       u32 rx_unicast_pkts;
+       /** Receive Broadcast Packet Count. */
+       u32 rx_broadcast_pkts;
+       /** Receive Multicast Packet Count. */
+       u32 rx_multicast_pkts;
+       /** Receive FCS Error Packet Count. */
+       u32 rx_fcserror_pkts;
+       /** Receive Undersize Good Packet Count. */
+       u32 rx_under_size_good_pkts;
+       /** Receive Oversize Good Packet Count. */
+       u32 rx_oversize_good_pkts;
+       /** Receive Undersize Error Packet Count. */
+       u32 rx_under_size_error_pkts;
+       /** Receive Good Pause Packet Count. */
+       u32 rx_good_pause_pkts;
+       /** Receive Oversize Error Packet Count. */
+       u32 rx_oversize_error_pkts;
+       /** Receive Align Error Packet Count. */
+       u32 rx_align_error_pkts;
+       /** Filtered Packet Count. */
+       u32 rx_filtered_pkts;
+       /** Receive Size 64 Bytes Packet Count. */
+       u32 rx64byte_pkts;
+       /** Receive Size 65-127 Bytes Packet Count. */
+       u32 rx127byte_pkts;
+       /** Receive Size 128-255 Bytes Packet Count. */
+       u32 rx255byte_pkts;
+       /** Receive Size 256-511 Bytes Packet Count. */
+       u32 rx511byte_pkts;
+       /** Receive Size 512-1023 Bytes Packet Count. */
+       u32 rx1023byte_pkts;
+       /** Receive Size 1024-1522 Bytes (or more, if configured) Packet Count. */
+       u32 rx_max_byte_pkts;
+       /** Overall Transmit Good Packets Count. */
+       u32 tx_good_pkts;
+       /** Transmit Unicast Packet Count. */
+       u32 tx_unicast_pkts;
+       /** Transmit Broadcast Packet Count. */
+       u32 tx_broadcast_pkts;
+       /** Transmit Multicast Packet Count. */
+       u32 tx_multicast_pkts;
+       /** Transmit Single Collision Count. */
+       u32 tx_single_coll_count;
+       /** Transmit Multiple Collision Count. */
+       u32 tx_mult_coll_count;
+       /** Transmit Late Collision Count. */
+       u32 tx_late_coll_count;
+       /** Transmit Excessive Collision Count. */
+       u32 tx_excess_coll_count;
+       /** Transmit Collision Count. */
+       u32 tx_coll_count;
+       /** Transmit Pause Packet Count. */
+       u32 tx_pause_count;
+       /** Transmit Size 64 Bytes Packet Count. */
+       u32 tx64byte_pkts;
+       /** Transmit Size 65-127 Bytes Packet Count. */
+       u32 tx127byte_pkts;
+       /** Transmit Size 128-255 Bytes Packet Count. */
+       u32 tx255byte_pkts;
+       /** Transmit Size 256-511 Bytes Packet Count. */
+       u32 tx511byte_pkts;
+       /** Transmit Size 512-1023 Bytes Packet Count. */
+       u32 tx1023byte_pkts;
+       /** Transmit Size 1024-1522 Bytes (or more, if configured) Packet Count. */
+       u32 tx_max_byte_pkts;
+       /** Transmit Drop Packet Count. */
+       u32 tx_dropped_pkts;
+       /** Transmit Dropped Packet Count, based on Congestion Management. */
+       u32 tx_acm_dropped_pkts;
+       /** Receive Dropped Packet Count. */
+       u32 rx_dropped_pkts;
+       /** Receive Good Byte Count (64 bit). */
+       u64 rx_good_bytes;
+       /** Receive Bad Byte Count (64 bit). */
+       u64 rx_bad_bytes;
+       /** Transmit Good Byte Count (64 bit). */
+       u64 tx_good_bytes;
+       /**For GSWIP V32 only **/
+       /** Receive Unicast Packet Count for Yellow & Red packet. */
+       u32 rx_unicast_pkts_yellow_red;
+       /** Receive Broadcast Packet Count for Yellow & Red packet. */
+       u32 rx_broadcast_pkts_yellow_red;
+       /** Receive Multicast Packet Count for Yellow & Red packet. */
+       u32 rx_multicast_pkts_yellow_red;
+       /** Receive Good Byte Count (64 bit) for Yellow & Red packet. */
+       u64 rx_good_bytes_yellow_red;
+       /** Receive Packet Count for Yellow & Red packet.  */
+       u32 rx_good_pkts_yellow_red;
+       /** Transmit Unicast Packet Count for Yellow & Red packet. */
+       u32 tx_unicast_pkts_yellow_red;
+       /** Transmit Broadcast Packet Count for Yellow & Red packet. */
+       u32 tx_broadcast_pkts_yellow_red;
+       /** Transmit Multicast Packet Count for Yellow & Red packet. */
+       u32 tx_multicast_pkts_yellow_red;
+       /** Transmit Good Byte Count (64 bit) for Yellow & Red packet. */
+       u64 tx_good_bytes_yellow_red;
+       /** Transmit Packet Count for Yellow & Red packet.  */
+       u32 tx_good_pkts_yellow_red;
+} mxl862xx_debug_rmon_port_cnt_t;
+
+#pragma scalar_storage_order default
+#pragma pack(pop)
+
+#endif /* _MXL862XX_RMON_H_ */
diff --git a/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_types.h b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_types.h
new file mode 100644 (file)
index 0000000..e65906c
--- /dev/null
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * drivers/net/dsa/host_api/mxl862xx_types.h - DSA Driver for MaxLinear Mxl862xx switch chips family
+ *
+ * Copyright (C) 2024 MaxLinear Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#ifndef _MXL862XX_TYPES_H_
+#define _MXL862XX_TYPES_H_
+
+#include <linux/phy.h>
+
+/** \brief This is the unsigned 64-bit datatype. */
+typedef uint64_t u64;
+/** \brief This is the unsigned 32-bit datatype. */
+typedef uint32_t u32;
+/** \brief This is the unsigned 16-bit datatype. */
+typedef uint16_t u16;
+/** \brief This is the unsigned 8-bit datatype. */
+typedef uint8_t u8;
+/** \brief This is the signed 64-bit datatype. */
+typedef int64_t i64;
+/** \brief This is the signed 32-bit datatype. */
+typedef int32_t i32;
+/** \brief This is the signed 16-bit datatype. */
+typedef int16_t i16;
+/** \brief This is the signed 8-bit datatype. */
+typedef int8_t i8;
+/** \brief This is the signed 8-bit datatype. */
+typedef int32_t s32;
+/** \brief This is the signed 8-bit datatype. */
+typedef int8_t s8;
+
+
+/** \brief This is a union to describe the IPv4 and IPv6 Address in numeric representation. Used by multiple Structures and APIs. The member selection would be based upon \ref GSW_IP_Select_t */
+typedef union {
+       /** Describe the IPv4 address.
+           Only used if the IPv4 address should be read or configured.
+           Cannot be used together with the IPv6 address fields. */
+       u32 nIPv4;
+       /** Describe the IPv6 address.
+           Only used if the IPv6 address should be read or configured.
+           Cannot be used together with the IPv4 address fields. */
+       u16 nIPv6[8];
+} mxl862xx_ip_t;
+
+/** \brief Selection to use IPv4 or IPv6.
+    Used  along with \ref GSW_IP_t to denote which union member to be accessed.
+*/
+typedef enum {
+       /** IPv4 Type */
+       MXL862XX_IP_SELECT_IPV4 = 0,
+       /** IPv6 Type */
+       MXL862XX_IP_SELECT_IPV6 = 1
+} mxl862xx_ip_select_t;
+
+typedef struct {
+       int sw_addr;
+       struct mii_bus *bus;
+} mxl862xx_device_t;
+#endif /* _MXL862XX_TYPES_H_ */
diff --git a/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/mxl862xx.c b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/mxl862xx.c
new file mode 100755 (executable)
index 0000000..7c4c967
--- /dev/null
@@ -0,0 +1,4286 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * drivers/net/dsa/mxl862xx.c - DSA Driver for MaxLinear Mxl862xx switch chips family
+ *
+ * Copyright (C) 2024 MaxLinear Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/etherdevice.h>
+#include <linux/if_bridge.h>
+#include <linux/if_vlan.h>
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include <linux/of_device.h>
+#include <linux/phy.h>
+#include <net/dsa.h>
+#include <linux/dsa/8021q.h>
+#include "host_api/mxl862xx_api.h"
+#include "host_api/mxl862xx_mmd_apis.h"
+#include "host_api/mxl862xx_mdio_relay.h"
+
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#else
+#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))
+#endif
+
+
+#define MAX_BRIDGES 16
+#define MAX_PORTS 13
+#define MAX_VLAN_ENTRIES (1024-160)
+#define IDX_INVAL (-1)
+
+#define VID_RULES 2
+#define INGRESS_FINAL_RULES 5
+#define INGRESS_VID_RULES VID_RULES
+#define EGRESS_FINAL_RULES 3
+#define EGRESS_VID_RULES VID_RULES
+/* It's only the size of the array for storing VLAN info.
+ * The real number of simultaneous VLANS is lower
+ * and depends on the number of filtering rules and ports.
+ * It is calculated dynamically at runtime. */
+#define MAX_VLANS  100
+#define MAX_RULES_RECYCLED MAX_VLANS
+
+
+/* Index of the array is bridgeID */
+u16 mxl862xx_bridge_portmap[MAX_BRIDGES] = { 0 };
+
+struct mxl862xx_hw_info {
+       uint8_t max_ports;
+       uint8_t phy_ports;
+       uint8_t cpu_port;
+       const struct dsa_switch_ops *ops;
+};
+
+struct mxl862xx_filter_ids {
+       int16_t filters_idx[VID_RULES];
+       bool valid;
+};
+
+struct mxl862xx_vlan {
+       bool used;
+       uint16_t vid;
+       /* indexes of filtering rules(entries) used for this VLAN */
+       int16_t filters_idx[VID_RULES];
+       /* Indicates if tags are added for eggress direction. Makes sense only in egress block */
+       bool untagged;
+};
+
+struct mxl862xx_extended_vlan_block_info {
+       bool allocated;
+       /* current index of the VID related filtering rules  */
+       uint16_t vid_filters_idx;
+       /* current index of the final filtering rules;
+        * counted backwards starting from the block end */
+       uint16_t final_filters_idx;
+       /* number of allocated  entries for filtering rules */
+       uint16_t filters_max;
+       /* number of entries per vlan */
+       uint16_t entries_per_vlan;
+       uint16_t block_id;
+       /* use this for storing indexes of vlan entries
+        * for VLAN delete */
+       struct mxl862xx_vlan vlans[MAX_VLANS];
+       /* collect released filter entries (pairs) that can be reused */
+       struct mxl862xx_filter_ids filter_entries_recycled[MAX_VLANS];
+};
+
+struct mxl862xx_port_vlan_info {
+       uint16_t pvid;
+       bool filtering;
+       /* Allow one-time initial vlan_filtering port attribute configuration. */
+       bool filtering_mode_locked;
+       /* Only one block can be assigned per port and direction. Take care about releasing the previous one when
+        * overwriting with the new one.*/
+       struct mxl862xx_extended_vlan_block_info ingress_vlan_block_info;
+       struct mxl862xx_extended_vlan_block_info egress_vlan_block_info;
+};
+
+struct mxl862xx_port_info {
+       bool port_enabled;
+       bool isolated;
+       uint16_t bridgeID;
+       uint16_t bridge_port_cpu;
+       struct net_device *bridge;
+       enum dsa_tag_protocol tag_protocol;
+       bool ingress_mirror_enabled;
+       bool egress_mirror_enabled;
+       struct mxl862xx_port_vlan_info vlan;
+};
+
+struct mxl862xx_priv {
+       struct mii_bus *bus;
+       struct device *dev;
+       int sw_addr;
+       const struct mxl862xx_hw_info *hw_info;
+       struct mxl862xx_port_info port_info[MAX_PORTS];
+       /* Number of simultaneously supported vlans (calculated in the runtime) */
+       uint16_t max_vlans;
+       /* pce_table_lock required for kernel 5.16 or later,
+        * since rtnl_lock has been dropped from DSA.port_fdb_{add,del}
+        * might cause dead-locks / hang in previous versions */
+       struct mutex pce_table_lock;
+};
+
+/* The mxl862_8021q 4-byte tagging is not yet supported in
+ * kernels >= 5.16 due to differences in DSA 8021q tagging handlers.
+ * DSA tx/rx vid functions are not avaliable, so dummy
+ * functions are here to make the code compilable. */
+static u16 dsa_8021q_rx_vid(struct dsa_switch *ds, int port)
+{
+       return 0;
+}
+
+static u16 dsa_8021q_tx_vid(struct dsa_switch *ds, int port)
+{
+       return 0;
+}
+
+static int mxl862xx_port_bridge_join(struct dsa_switch *ds, int port,
+                                  struct dsa_bridge br,
+                                  bool *tx_fwd_offload,
+                                  struct netlink_ext_ack *extack);
+static void mxl862xx_port_bridge_leave(struct dsa_switch *ds, int port,
+                                      struct dsa_bridge br);
+
+static int __update_bridge_conf_port(struct dsa_switch *ds, uint8_t port,
+                                    struct net_device *bridge, int action);
+
+static int __deactivate_vlan_filter_entry(u16 block_id, u16 entry_idx);
+
+static int __prepare_vlan_ingress_filters_sp_tag_cpu(struct dsa_switch *ds,
+                                               uint8_t port, uint8_t cpu_port);
+static int __prepare_vlan_egress_filters_off_sp_tag_cpu(struct dsa_switch *ds,
+                                               uint8_t cpu_port);
+static int __prepare_vlan_ingress_filters_off_sp_tag_no_vid(struct dsa_switch *ds,
+                                               uint8_t port);
+static int __prepare_vlan_egress_filters_off_sp_tag_no_vid(struct dsa_switch *ds,
+                                               uint8_t port);
+
+static int mxl862xx_port_vlan_add(struct dsa_switch *ds, int port,
+                                 const struct switchdev_obj_port_vlan *vlan,
+                                 struct netlink_ext_ack *extack);
+static int mxl862xx_port_vlan_del(struct dsa_switch *ds, int port,
+                                 const struct switchdev_obj_port_vlan *vlan);
+
+static mxl862xx_device_t mxl_dev;
+
+struct mxl862xx_rmon_cnt_desc {
+       unsigned int size;
+       unsigned int offset;
+       const char *name;
+};
+
+#define MIB_DESC(_name)       \
+       {                     \
+               .name = _name \
+       }
+
+static const struct mxl862xx_rmon_cnt_desc mxl862xx_rmon_cnt[] = {
+       MIB_DESC("RxGoodPkts"), //0
+       MIB_DESC("RxUnicastPkts"),
+       MIB_DESC("RxBroadcastPkts"),
+       MIB_DESC("RxMulticastPkts"),
+       MIB_DESC("RxFCSErrorPkts"),
+       MIB_DESC("RxUnderSizeGoodPkts"),
+       MIB_DESC("RxOversizeGoodPkts"),
+       MIB_DESC("RxUnderSizeErrorPkts"),
+       MIB_DESC("RxOversizeErrorPkts"),
+       MIB_DESC("RxFilteredPkts"),
+       MIB_DESC("Rx64BytePkts"), //10
+       MIB_DESC("Rx127BytePkts"),
+       MIB_DESC("Rx255BytePkts"),
+       MIB_DESC("Rx511BytePkts"),
+       MIB_DESC("Rx1023BytePkts"),
+       MIB_DESC("RxMaxBytePkts"),
+       MIB_DESC("RxDroppedPkts"),
+       MIB_DESC("RxExtendedVlanDiscardPkts"),
+       MIB_DESC("MtuExceedDiscardPkts"),
+       MIB_DESC("RxGoodBytes"),
+       MIB_DESC("RxBadBytes"), //20
+       MIB_DESC("RxUnicastPktsYellowRed"),
+       MIB_DESC("RxBroadcastPktsYellowRed"),
+       MIB_DESC("RxMulticastPktsYellowRed"),
+       MIB_DESC("RxGoodPktsYellowRed"),
+       MIB_DESC("RxGoodBytesYellowRed"),
+       MIB_DESC("RxGoodPausePkts"),
+       MIB_DESC("RxAlignErrorPkts"),
+       //-----------------------------
+       MIB_DESC("TxGoodPkts"),
+       MIB_DESC("TxUnicastPkts"),
+       MIB_DESC("TxBroadcastPkts"), //30
+       MIB_DESC("TxMulticastPkts"),
+       MIB_DESC("Tx64BytePkts"),
+       MIB_DESC("Tx127BytePkts"),
+       MIB_DESC("Tx255BytePkts"),
+       MIB_DESC("Tx511BytePkts"),
+       MIB_DESC("Tx1023BytePkts"),
+       MIB_DESC("TxMaxBytePkts"),
+       MIB_DESC("TxDroppedPkts"),
+       MIB_DESC("TxAcmDroppedPkts"),
+       MIB_DESC("TxGoodBytes"), //40
+       MIB_DESC("TxUnicastPktsYellowRed"),
+       MIB_DESC("TxBroadcastPktsYellowRed"),
+       MIB_DESC("TxMulticastPktsYellowRed"),
+       MIB_DESC("TxGoodPktsYellowRed"),
+       MIB_DESC("TxGoodBytesYellowRed"),
+       MIB_DESC("TxSingleCollCount"),
+       MIB_DESC("TxMultCollCount"),
+       MIB_DESC("TxLateCollCount"),
+       MIB_DESC("TxExcessCollCount"),
+       MIB_DESC("TxCollCount"), //50
+       MIB_DESC("TxPauseCount"),
+};
+
+static int mxl862xx_phy_read_mmd(struct dsa_switch *ds, int devnum, int port, int reg)
+{
+       int ret = MXL862XX_STATUS_ERR;
+       struct mdio_relay_data param = { 0 };
+
+       param.phy = port;
+       param.mmd = devnum;
+       param.reg = reg & 0xffff;
+
+       ret = int_gphy_read(&mxl_dev, &param);
+       if (ret == MXL862XX_STATUS_OK)
+               goto EXIT;
+       else {
+               pr_err("%s: reading port: %d failed with err: %d\n",
+                       __func__, port, ret);
+       }
+
+       return ret;
+
+EXIT:
+       return param.data;
+}
+
+static int mxl862xx_phy_write_mmd(struct dsa_switch *ds, int devnum, int port, int reg,
+                             u16 data)
+{
+       int ret = MXL862XX_STATUS_ERR;
+       struct mdio_relay_data param = { 0 };
+
+       param.phy = port;
+       param.mmd = devnum;
+       param.reg = reg;
+       param.data = data;
+
+       ret = int_gphy_write(&mxl_dev, &param);
+       if (ret == MXL862XX_STATUS_OK)
+               goto EXIT;
+       else {
+               pr_err("%s: writing port: %d failed with err: %d\n",
+                       __func__, port, ret);
+       }
+
+EXIT:
+       return ret;
+}
+
+static int mxl862xx_phy_read(struct dsa_switch *ds, int port, int reg)
+{
+       int ret = mxl862xx_phy_read_mmd(ds, 0, port, reg);
+       return ret;
+}
+
+static int mxl862xx_phy_write(struct dsa_switch *ds, int port, int reg,
+                             u16 data)
+{
+       int ret = mxl862xx_phy_write_mmd(ds, 0, port, reg, data);
+       return ret;
+}
+
+static int mxl862xx_mmd_write(const mxl862xx_device_t *dev, int reg, u16 data)
+{
+       int ret;
+       /* lock mdio bus */
+       mutex_lock_nested(&dev->bus->mdio_lock, MDIO_MUTEX_NESTED);
+       ret = mxl862xx_write(dev, reg, data);
+       /* unlock mdio bus */
+       mutex_unlock(&dev->bus->mdio_lock);
+
+       return ret;
+}
+
+static int __config_mxl862_tag_proto(struct dsa_switch *ds, uint8_t port, bool enable)
+{
+
+       int ret = MXL862XX_STATUS_ERR;
+       uint8_t pid = port + 1;
+       mxl862xx_ss_sp_tag_t param = { 0 };
+       mxl862xx_ctp_port_assignment_t ctp_port_assign = { 0 };
+
+       param.pid = pid;
+       param.mask = BIT(0) | BIT(1);
+       if (enable) {
+               param.rx = 2;
+               param.tx = 2;
+       } else {
+               param.rx = 1;
+               param.tx = 3;
+       }
+
+       ret = mxl862xx_ss_sp_tag_set(&mxl_dev, &param);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(ds->dev,
+               "%s: Error %d while configuring DSA frame tagging for port:%d\n",
+               __func__, ret, port);
+               goto EXIT;
+       }
+
+       if (enable)
+               ctp_port_assign.number_of_ctp_port = 32 - pid;
+       else
+               ctp_port_assign.number_of_ctp_port = 1;
+
+       ctp_port_assign.logical_port_id = pid;
+       ctp_port_assign.first_ctp_port_id = pid;
+       ctp_port_assign.mode = MXL862XX_LOGICAL_PORT_GPON;
+
+       ret = mxl862xx_ctp_port_assignment_set(&mxl_dev, &ctp_port_assign);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(ds->dev,
+                       "%s: Error %d while configuring assignment of port %d for DSA frame tagging.\n",
+                       __func__, ret, port);
+               goto EXIT;
+       }
+
+EXIT:
+       return ret;
+}
+
+static void mxl862xx_port_disable(struct dsa_switch *ds, int port)
+{
+       struct mxl862xx_priv *priv = ds->priv;
+       mxl862xx_register_mod_t register_mod = { 0 };
+       int ret = 0;
+
+       if (!dsa_is_user_port(ds, port))
+               return;
+
+       /* Disable datapath */
+       register_mod.reg_addr = MxL862XX_SDMA_PCTRLp(port + 1);
+       register_mod.data = 0;
+       register_mod.mask = MXL862XX_SDMA_PCTRL_EN;
+       ret = mxl862xx_register_mod(&mxl_dev, &register_mod);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(ds->dev, "%s: Disable Datapath SDMA failed:%d\n",
+                       __func__, port);
+               return;
+       }
+       register_mod.reg_addr = MxL862XX_FDMA_PCTRLp(port + 1);
+       register_mod.data = 0;
+       register_mod.mask = MXL862XX_FDMA_PCTRL_EN;
+       ret = mxl862xx_register_mod(&mxl_dev, &register_mod);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(ds->dev, "%s: Disable Datapath FDMA failed:%d\n",
+                       __func__, port);
+               return;
+       }
+
+       priv->port_info[port].port_enabled = false;
+}
+
+static int mxl862xx_port_enable(struct dsa_switch *ds, int port,
+                               struct phy_device *phydev)
+{
+       struct mxl862xx_priv *priv = ds->priv;
+       mxl862xx_register_mod_t register_mod = { 0 };
+       int ret;
+
+       if (!dsa_is_user_port(ds, port))
+               return 0;
+
+       if (!dsa_is_cpu_port(ds, port)) {
+               /* Enable datapath */
+               register_mod.reg_addr = MxL862XX_SDMA_PCTRLp(port + 1);
+               register_mod.data = MXL862XX_SDMA_PCTRL_EN;
+               register_mod.mask = MXL862XX_SDMA_PCTRL_EN;
+               ret = mxl862xx_register_mod(&mxl_dev, &register_mod);
+               if (ret != MXL862XX_STATUS_OK) {
+                       dev_err(ds->dev,
+                               "%s: Enable Datapath SDMA failed:%d\n",
+                               __func__, port);
+                       return ret;
+               }
+               register_mod.reg_addr = MxL862XX_FDMA_PCTRLp(port + 1);
+               register_mod.data = MXL862XX_FDMA_PCTRL_EN;
+               register_mod.mask = MXL862XX_FDMA_PCTRL_EN;
+               ret = mxl862xx_register_mod(&mxl_dev, &register_mod);
+               if (ret != MXL862XX_STATUS_OK) {
+                       dev_err(ds->dev,
+                               "%s: Enable Datapath FDMA failed:%d\n",
+                               __func__, port);
+                       return ret;
+               }
+       }
+
+       priv->port_info[port].port_enabled = true;
+
+       return 0;
+}
+
+static int __isolate_port(struct dsa_switch *ds, uint8_t port)
+{
+       mxl862xx_bridge_alloc_t br_alloc = { 0 };
+       struct mxl862xx_priv *priv = ds->priv;
+       int ret = -EINVAL;
+       uint8_t cpu_port = priv->hw_info->cpu_port;
+       bool vlan_sp_tag = (priv->port_info[cpu_port].tag_protocol == DSA_TAG_PROTO_MXL862_8021Q);
+
+       /* Exit if port is already isolated */
+       if (priv->port_info[port].isolated)
+               return ret;
+
+       ret = mxl862xx_bridge_alloc(&mxl_dev, &br_alloc);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(ds->dev,
+                       "%s: bridge alloc failed for port %d\n, ret:%d",
+                       __func__, port, ret);
+               return ret;
+       }
+
+       priv->port_info[port].bridgeID = br_alloc.bridge_id;
+       priv->port_info[port].bridge = NULL;
+
+       ret = __update_bridge_conf_port(ds, port, NULL, 1);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(ds->dev,
+                       "%s: bridge port adding failed for port %d, ret %d\n",
+                       __func__, port, ret);
+       }
+
+       /* for VLAN special tagging mode add port to vlan 1 to apply also
+        * the special tag handling filters */
+       if (vlan_sp_tag) {
+               /* set port vlan 1 untagged */
+               struct switchdev_obj_port_vlan vlan;
+               uint16_t vid = 1;
+               bool filtering_prev = priv->port_info[port].vlan.filtering;
+
+               priv->port_info[port].vlan.filtering = true;
+               vlan.flags = BRIDGE_VLAN_INFO_UNTAGGED|BRIDGE_VLAN_INFO_PVID;
+               vlan.vid = vid;
+               ret = mxl862xx_port_vlan_add(ds, port, &vlan, NULL);
+               if (ret != MXL862XX_STATUS_OK) {
+                       dev_err(ds->dev,
+                               "%s: adding port %d, to vlan:%d failed with ret %d\n",
+                               __func__, port, vlan.vid, ret);
+               }
+
+               priv->port_info[port].vlan.filtering = filtering_prev;
+       }
+
+       priv->port_info[port].isolated = true;
+       return ret;
+}
+
+static void __deisolate_port(struct dsa_switch *ds, uint8_t port)
+{
+       mxl862xx_bridge_alloc_t br_alloc = { 0 };
+       struct mxl862xx_priv *priv = ds->priv;
+       uint8_t cpu_port = priv->hw_info->cpu_port;
+       bool vlan_sp_tag = (priv->port_info[cpu_port].tag_protocol == DSA_TAG_PROTO_MXL862_8021Q);
+       int ret = -EINVAL;
+
+       /* Check if port is isolated. The isolation with standalone bridges was
+        * done skipping the linux bridge layer so bridge should be NULL */
+       if (!priv->port_info[port].isolated)
+               return;
+
+       ret = __update_bridge_conf_port(ds, port, NULL, 0);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(ds->dev,
+                       "%s: bridge port removal failed for port %d, ret %d\n",
+                       __func__, port, ret);
+               return;
+       }
+
+       br_alloc.bridge_id = priv->port_info[port].bridgeID;
+       ret = mxl862xx_bridge_free(&mxl_dev, &br_alloc);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(ds->dev,
+                       "%s: bridge free failed for port:%d, BridgeID: %d, ret: %d\n",
+                       __func__, port, br_alloc.bridge_id, ret);
+               return;
+       }
+
+       /* For VLAN special tagging mode isolated port is assigned to VLAN 1
+        * to apply also the special tag handling filters. Now for deisolation
+        * VLAN 1 must be unassigned */
+       if (vlan_sp_tag) {
+               struct switchdev_obj_port_vlan vlan;
+               uint16_t vid = 1;
+               uint16_t i;
+
+               vlan.flags = BRIDGE_VLAN_INFO_UNTAGGED|BRIDGE_VLAN_INFO_PVID;
+               vlan.vid = vid;
+               /* Removes vid dependant 'dynamic' rules */
+               ret = mxl862xx_port_vlan_del(ds, port, &vlan);
+               if (ret != MXL862XX_STATUS_OK) {
+                       dev_err(ds->dev,
+                               "%s: deleting port %d, from vlan:%d failed with ret %d\n",
+                               __func__, port, vid, ret);
+               }
+
+               /* Clear/deactivate 'static' set of filtering rules, placed at the end of the block */
+               for (i = 0 ; i < 2 ; i++) {
+                       uint16_t j, start_idx, stop_idx, block_id;
+                       struct mxl862xx_extended_vlan_block_info *block_info = (i == 0)
+                               ? &priv->port_info[port].vlan.ingress_vlan_block_info
+                               : &priv->port_info[port].vlan.egress_vlan_block_info;
+
+                       block_id = block_info->block_id;
+                       stop_idx = block_info->filters_max;
+                       start_idx = block_info->final_filters_idx;
+
+                       for (j = start_idx ; j < stop_idx ; j++) {
+                               ret = __deactivate_vlan_filter_entry(block_id, j);
+                               if (ret != MXL862XX_STATUS_OK)
+                                       return;
+                       }
+                       /* Entries cleared, so point out to the end */
+                       block_info->final_filters_idx = j;
+               }
+       }
+
+       priv->port_info[port].isolated = false;
+
+       return;
+}
+
+static int __update_bridge_conf_port(struct dsa_switch *ds, uint8_t port,
+                                    struct net_device *bridge, int action)
+{
+       int ret = -EINVAL;
+       uint8_t i;
+       struct mxl862xx_priv *priv = ds->priv;
+       uint8_t phy_ports = priv->hw_info->phy_ports;
+       uint8_t cpu_port = priv->hw_info->cpu_port;
+       bool vlan_sp_tag = (priv->port_info[cpu_port].tag_protocol == DSA_TAG_PROTO_MXL862_8021Q);
+
+       /* Update local bridge port map */
+       for (i = 0; i < phy_ports; i++) {
+               int bridgeID = priv->port_info[i].bridgeID;
+
+               if (!((struct dsa_port *)dsa_to_port(ds, i)))
+                       continue;
+
+               /* CPU port is assigned to all bridges and cannot be modified  */
+               if ((dsa_is_cpu_port(ds, i)))
+                       continue;
+
+               /* Skip if bridge does not match, except the self port assignment  */
+               if ((dsa_port_bridge_dev_get(dsa_to_port(ds, i)) != bridge) && (i != port))
+                       continue;
+
+               /* Case for standalone bridges assigned only to single user and CPU ports.
+                * Used only for initial ports isolation */
+               if ((bridge == NULL) && (i != port))
+                       continue;
+
+               if (action) {
+                       /* Add port to bridge portmap */
+                       mxl862xx_bridge_portmap[bridgeID] |= BIT(port + 1);
+               } else {
+                       /* Remove port from the bitmap */
+                       mxl862xx_bridge_portmap[bridgeID] &= ~BIT(port + 1);
+               }
+       }
+
+       /* Update switch according to local bridge port map */
+       /* Add this port to the port maps of other ports skiping it's own map */
+       for (i = 0; i < phy_ports; i++) {
+               mxl862xx_bridge_port_config_t br_port_cfg = { 0 };
+               int bridgeID = priv->port_info[i].bridgeID;
+
+               if (!(dsa_is_user_port(ds, i)))
+                       continue;
+
+               /* Case for standalone bridges assigned only to single user and CPU ports.
+                * Used only for initial ports isolation */
+               if ((bridge == NULL) && (i != port))
+                       continue;
+
+               /* Do not reconfigure any standalone bridge if this is bridge join scenario */
+               if ((bridge != NULL) && (priv->port_info[i].bridge == NULL))
+                       continue;
+
+               br_port_cfg.bridge_port_id = i + 1;
+               br_port_cfg.mask |=
+                       MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP |
+                       MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID;
+
+               ret = mxl862xx_bridge_port_config_get(&mxl_dev, &br_port_cfg);
+               if (ret != MXL862XX_STATUS_OK) {
+                       dev_err(ds->dev,
+                               "%s: Bridge port configuration for port %d failed with %d\n",
+                               __func__, port, ret);
+                       goto EXIT;
+               }
+
+               /* Skip port map update if for the existing bridge the port
+                * is not assigned there. */
+               if (i != port && (br_port_cfg.bridge_id != bridgeID ||
+                                 br_port_cfg.bridge_id == 0))
+                       continue;
+
+               br_port_cfg.mask |=
+                       MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP |
+                       MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID |
+                       MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING;
+
+               /* Skip the port itself in it's own portmap */
+               br_port_cfg.bridge_port_map[0] =
+                       mxl862xx_bridge_portmap[bridgeID] & ~(BIT(i + 1));
+
+               if (action) {
+                       /* If bridge is null then this is port isolation scenario. Disable MAC learning. */
+                       br_port_cfg.src_mac_learning_disable =
+                               (bridge == NULL) ? true : false;
+                       br_port_cfg.bridge_id = bridgeID;
+               }
+               /* When port is removed from the bridge, assign it back to the default
+                * bridge 0 */
+               else {
+                       br_port_cfg.src_mac_learning_disable = true;
+                       /* Cleanup the port own map leaving only the CPU port mapping. */
+                       if (i == port) {
+                               br_port_cfg.bridge_port_map[0] =
+                                       BIT(cpu_port + 1);
+                               br_port_cfg.bridge_id = 0;
+                       }
+               }
+
+               ret = mxl862xx_bridge_port_config_set(&mxl_dev, &br_port_cfg);
+               if (ret != MXL862XX_STATUS_OK) {
+                       dev_err(ds->dev,
+                               "%s: Bridge port configuration for port %d failed with ret=%d\n",
+                               __func__, port, ret);
+                       goto EXIT;
+               }
+       }
+
+       /* Configure additional bridge port for VLAN based tagging */
+       if (vlan_sp_tag) {
+               int bridgeID = priv->port_info[port].bridgeID;
+               uint16_t bridge_port_cpu = port + 1 + 16;
+               mxl862xx_bridge_port_alloc_t bpa_param = { 0 };
+               mxl862xx_bridge_port_config_t br_port_cfg = { 0 };
+
+               bpa_param.bridge_port_id = bridge_port_cpu;
+
+               if (action) {
+                       ret = mxl862xx_bridge_port_alloc(&mxl_dev, &bpa_param);
+                       if (ret != MXL862XX_STATUS_OK) {
+                               dev_err(ds->dev,
+                                       "%s: Port:%d failed to prepare associated bridge port\n",
+                                       __func__, port);
+                               goto EXIT;
+                       }
+
+                       br_port_cfg.mask |=
+                               MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP |
+                               MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID |
+                               MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_CTP_MAPPING |
+                               MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING;
+                       br_port_cfg.bridge_id = bridgeID;
+                       br_port_cfg.bridge_port_id = bridge_port_cpu;
+                       br_port_cfg.bridge_port_map[0] =        BIT(port + 1);
+                       br_port_cfg.dest_logical_port_id = cpu_port + 1;
+                       br_port_cfg.src_mac_learning_disable = true;
+
+                       ret = mxl862xx_bridge_port_config_set(&mxl_dev, &br_port_cfg);
+
+                       if (ret != MXL862XX_STATUS_OK) {
+                               dev_err(ds->dev,
+                               "%s: Configuration of cpu bridge port:%d  for port:%d on bridge:%d failed with ret=%d\n",
+                               __func__, bridge_port_cpu, port, bridgeID, ret);
+                               goto EXIT;
+                       }
+
+                       /* Add bridge cpu port to portmap */
+                       mxl862xx_bridge_portmap[bridgeID] |= BIT(bridge_port_cpu);
+                       priv->port_info[port].bridge_port_cpu = bridge_port_cpu;
+               }
+               /* remove */
+               else {
+                       ret = mxl862xx_bridge_port_free(&mxl_dev, &bpa_param);
+                       if (ret != MXL862XX_STATUS_OK) {
+                               dev_err(ds->dev,
+                                       "%s: Port:%d failed to free associated bridge port\n",
+                                       __func__, port);
+                               goto EXIT;
+                       }
+                       /* Remove bridge cpu port from portmap */
+                       mxl862xx_bridge_portmap[bridgeID] &= ~BIT(bridge_port_cpu);
+                       priv->port_info[port].bridge_port_cpu = 0;
+               }
+
+       }
+
+EXIT:
+       return ret;
+}
+
+/* Returns bridgeID, or 0 if bridge not found */
+static int __find_bridgeID(struct dsa_switch *ds, struct net_device *bridge)
+{
+       uint8_t i;
+       struct mxl862xx_priv *priv = ds->priv;
+       uint8_t phy_ports = priv->hw_info->phy_ports;
+       int bridgeID = 0;
+
+       /* The only use case where bridge is NULL is the isolation
+        * with individual port bridges configured in the setup function. */
+       if (bridge == NULL)
+               return bridgeID;
+
+       for (i = 0; i < phy_ports; i++) {
+               if (priv->port_info[i].bridge == bridge) {
+                       bridgeID = priv->port_info[i].bridgeID;
+                       break;
+               }
+       }
+       return bridgeID;
+}
+
+static void __set_vlan_filters_limits(struct dsa_switch *ds)
+{
+       uint8_t i;
+       uint16_t cpu_ingress_entries;
+       uint16_t cpu_egress_entries;
+       uint16_t user_ingress_entries;
+       uint16_t user_egress_entries;
+       struct mxl862xx_priv *priv = ds->priv;
+       uint8_t cpu_port = priv->hw_info->cpu_port;
+
+       /* Set limits and indexes required for processing VLAN rules for CPU port */
+
+       /* The calculation of the max number of simultaneously supported VLANS (priv->max_vlans)
+        * comes from the equation:
+        *
+        * MAX_VLAN_ENTRIES = phy_ports * (EGRESS_FINAL_RULES + EGRESS_VID_RULES * priv->max_vlans)
+        *  + phy_ports * (INGRESS_FINAL_RULES + INGRESS_VID_RULES * priv-> max_vlans)
+        *  + cpu_ingress_entries + cpu_egress_entries  */
+
+       if (priv->port_info[cpu_port].tag_protocol == DSA_TAG_PROTO_MXL862_8021Q) {
+
+               priv->max_vlans = (MAX_VLAN_ENTRIES - priv->hw_info->phy_ports *
+                               (EGRESS_FINAL_RULES + INGRESS_FINAL_RULES + 2) - 3) /
+                       (priv->hw_info->phy_ports * (EGRESS_VID_RULES + INGRESS_VID_RULES) + 2);
+               /* 2 entries per port and 1 entry for fixed rule */
+               cpu_ingress_entries = priv->hw_info->phy_ports * 2 + 1;
+               /* 2 entries per each vlan and 2 entries for fixed rules */
+               cpu_egress_entries = priv->max_vlans * 2 + 2;
+
+               //priv->port_info[cpu_port].vlan.ingress_vlan_block_info.entries_per_vlan = 2;
+               priv->port_info[cpu_port].vlan.ingress_vlan_block_info.entries_per_vlan = 0;
+               priv->port_info[cpu_port].vlan.ingress_vlan_block_info.filters_max = cpu_ingress_entries;
+               priv->port_info[cpu_port].vlan.egress_vlan_block_info.entries_per_vlan = 2;
+               priv->port_info[cpu_port].vlan.egress_vlan_block_info.filters_max = cpu_egress_entries;
+
+               user_ingress_entries = INGRESS_FINAL_RULES + INGRESS_VID_RULES * priv->max_vlans;
+               user_egress_entries = EGRESS_FINAL_RULES + EGRESS_VID_RULES * priv->max_vlans;
+       } else {
+               priv->max_vlans = (MAX_VLAN_ENTRIES - priv->hw_info->phy_ports *
+                               (EGRESS_FINAL_RULES + INGRESS_FINAL_RULES) - 1) /
+                       (priv->hw_info->phy_ports * (EGRESS_VID_RULES + INGRESS_VID_RULES) + 2);
+               /* 1 entry for fixed rule */
+               cpu_ingress_entries =  1;
+               /* 2 entries per each vlan  */
+               cpu_egress_entries = priv->max_vlans * 2;
+               priv->port_info[cpu_port].vlan.ingress_vlan_block_info.entries_per_vlan = 0;
+               priv->port_info[cpu_port].vlan.ingress_vlan_block_info.filters_max = cpu_ingress_entries;
+               priv->port_info[cpu_port].vlan.egress_vlan_block_info.entries_per_vlan = 2;
+               priv->port_info[cpu_port].vlan.egress_vlan_block_info.filters_max = cpu_egress_entries;
+
+               user_ingress_entries = INGRESS_FINAL_RULES + INGRESS_VID_RULES * priv->max_vlans;
+               user_egress_entries = EGRESS_FINAL_RULES + EGRESS_VID_RULES * priv->max_vlans;
+       }
+
+       /* This index is counted backwards */
+       priv->port_info[cpu_port].vlan.ingress_vlan_block_info.final_filters_idx =
+               priv->port_info[cpu_port].vlan.ingress_vlan_block_info.filters_max - 1;
+       priv->port_info[cpu_port].vlan.egress_vlan_block_info.final_filters_idx =
+               priv->port_info[cpu_port].vlan.egress_vlan_block_info.filters_max - 1;
+
+       /* Set limits and indexes required for processing VLAN rules for user ports */
+       for (i = 0; i < priv->hw_info->phy_ports; i++) {
+               priv->port_info[i].vlan.ingress_vlan_block_info.entries_per_vlan = INGRESS_VID_RULES;
+               priv->port_info[i].vlan.ingress_vlan_block_info.filters_max = user_ingress_entries;
+               priv->port_info[i].vlan.egress_vlan_block_info.entries_per_vlan = EGRESS_VID_RULES;
+               priv->port_info[i].vlan.egress_vlan_block_info.filters_max = user_egress_entries;
+               /* This index is counted backwards */
+               priv->port_info[i].vlan.ingress_vlan_block_info.final_filters_idx =
+                       priv->port_info[i].vlan.ingress_vlan_block_info.filters_max - 1;
+               priv->port_info[i].vlan.egress_vlan_block_info.final_filters_idx =
+                       priv->port_info[i].vlan.egress_vlan_block_info.filters_max - 1;
+       }
+       dev_info(ds->dev, "%s: phy_ports:%d, priv->max_vlans: %d, cpu_egress_entries: %d, user_ingress_entries: %d, INGRESS_VID_RULES: %d\n",
+                       __func__, priv->hw_info->phy_ports, priv->max_vlans,
+                       cpu_egress_entries, user_ingress_entries, INGRESS_VID_RULES);
+}
+
+static enum dsa_tag_protocol __dt_parse_tag_proto(struct dsa_switch *ds, uint8_t port)
+{
+       /* Default value if no dt entry found */
+       enum dsa_tag_protocol tag_proto = DSA_TAG_PROTO_MXL862;
+       struct dsa_port *dp = (struct dsa_port *)dsa_to_port(ds, port);
+       const char *user_protocol = NULL;
+
+       if (dp != NULL)
+               user_protocol = of_get_property(dp->dn, "dsa-tag-protocol", NULL);
+       if (user_protocol != NULL) {
+               if (strcmp("mxl862", user_protocol) == 0)
+                       tag_proto = DSA_TAG_PROTO_MXL862;
+               else if (strcmp("mxl862_8021q", user_protocol) == 0)
+                       tag_proto = DSA_TAG_PROTO_MXL862_8021Q;
+       }
+
+       return tag_proto;
+}
+
+static int mxl862xx_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
+{
+       int ret = -EINVAL;
+       mxl862xx_cfg_t cfg = { 0 };
+
+       ret =  mxl862xx_cfg_get(&mxl_dev, &cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(ds->dev, "%s: failed to read switch config\n", __func__);
+               goto EXIT;
+       }
+
+       cfg.mac_table_age_timer = MXL862XX_AGETIMER_CUSTOM;
+       cfg.age_timer = msecs / 1000;
+
+       ret =  mxl862xx_cfg_set(&mxl_dev, &cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(ds->dev, "%s: failed to configure global MAC ageing value: %ds\n",
+                       __func__, cfg.age_timer);
+       }
+
+EXIT:
+       return ret;
+}
+
+static int mxl862xx_port_bridge_join(struct dsa_switch *ds, int port,
+                                       struct dsa_bridge br, bool *tx_fwd_offload,
+                                       struct netlink_ext_ack *extack)
+
+{
+       struct net_device *bridge = br.dev;
+       struct mxl862xx_priv *priv = ds->priv;
+       int bridgeID = 0;
+       int ret = -EINVAL;
+       uint8_t cpu_port = priv->hw_info->cpu_port;
+       bool vlan_sp_tag = (priv->port_info[cpu_port].tag_protocol == DSA_TAG_PROTO_MXL862_8021Q);
+
+       if (port < 0 || port >= MAX_PORTS) {
+               dev_err(priv->dev, "invalid port: %d\n", port);
+               return ret;
+       }
+
+       __deisolate_port(ds, port);
+
+       bridgeID = __find_bridgeID(ds, bridge);
+
+       /* no bridge found -> create new bridge */
+       if (bridgeID == 0) {
+               mxl862xx_bridge_alloc_t br_alloc = { 0 };
+
+               ret = mxl862xx_bridge_alloc(&mxl_dev, &br_alloc);
+               if (ret != MXL862XX_STATUS_OK) {
+                       NL_SET_ERR_MSG_MOD(extack,
+                                  "MxL862xx: bridge alloc failed");
+                       dev_err(ds->dev,
+                               "%s: bridge alloc failed for port %d\n, ret:%d",
+                               __func__, port, ret);
+                       goto EXIT;
+               }
+               priv->port_info[port].bridgeID = br_alloc.bridge_id;
+               priv->port_info[port].bridge = bridge;
+               /* bridge found  */
+       } else {
+               priv->port_info[port].bridgeID = bridgeID;
+               priv->port_info[port].bridge = bridge;
+       }
+
+       ret = __update_bridge_conf_port(ds, port, bridge, 1);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(ds->dev,
+                       "%s: bridge port adding failed for port %d, ret %d\n",
+                       __func__, port, ret);
+               goto EXIT;
+       }
+       /* For some kernel versions for VLAN unaware bridges Linux calls .port_vlan_add,
+        * for others not. To support VLAN unaware bridge in mxl862_8021q tagging mode,
+        * the required vlan filtering rules (adding sp tag, forwarding to cpu port)
+        * are added here.*/
+       if (vlan_sp_tag) {
+               mxl862xx_ctp_port_config_t ctp_param = { 0 };
+               mxl862xx_bridge_port_config_t br_port_cfg = { 0 };
+
+               ret = __prepare_vlan_ingress_filters_sp_tag_cpu(ds, port, cpu_port);
+               if (ret != MXL862XX_STATUS_OK)
+                       goto EXIT;
+               ret = __prepare_vlan_egress_filters_off_sp_tag_cpu(ds, cpu_port);
+               if (ret != MXL862XX_STATUS_OK)
+                       goto EXIT;
+               ret = __prepare_vlan_ingress_filters_off_sp_tag_no_vid(ds, port);
+               if (ret != MXL862XX_STATUS_OK)
+                       goto EXIT;
+               ret = __prepare_vlan_egress_filters_off_sp_tag_no_vid(ds, port);
+               if (ret != MXL862XX_STATUS_OK)
+                       goto EXIT;
+
+               /* update cpu port */
+               ctp_param.logical_port_id = cpu_port + 1;
+               ctp_param.mask = MXL862XX_CTP_PORT_CONFIG_MASK_EGRESS_VLAN |
+                                    MXL862XX_CTP_PORT_CONFIG_MASK_INGRESS_VLAN;
+               ctp_param.egress_extended_vlan_enable = true;
+               ctp_param.egress_extended_vlan_block_id =
+               priv->port_info[cpu_port].vlan.egress_vlan_block_info.block_id;
+               ctp_param.ingress_extended_vlan_enable = true;
+               ctp_param.ingress_extended_vlan_block_id =
+                       priv->port_info[cpu_port].vlan.ingress_vlan_block_info.block_id;
+
+               ret = mxl862xx_ctp_port_config_set(&mxl_dev, &ctp_param);
+               if (ret != MXL862XX_STATUS_OK) {
+                       dev_err(ds->dev,
+                               "%s: CTP port %d config failed on port config set with %d\n",
+                               __func__, cpu_port, ret);
+                       NL_SET_ERR_MSG_MOD(extack, "Failed to configure VLAN for cpu port");
+                       goto EXIT;
+               }
+
+               /* Update bridge port */
+               br_port_cfg.bridge_port_id = port + 1;
+               br_port_cfg.mask |= MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN |
+                            MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN;
+               br_port_cfg.egress_extended_vlan_enable = true;
+               br_port_cfg.egress_extended_vlan_block_id =
+                       priv->port_info[port].vlan.egress_vlan_block_info.block_id;
+               br_port_cfg.ingress_extended_vlan_enable = true;
+               br_port_cfg.ingress_extended_vlan_block_id =
+                       priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+
+               ret = mxl862xx_bridge_port_config_set(&mxl_dev, &br_port_cfg);
+               if (ret != MXL862XX_STATUS_OK) {
+                       dev_err(ds->dev,
+                               "%s: Bridge port configuration for port %d failed with %d\n",
+                               __func__, port, ret);
+                       NL_SET_ERR_MSG_MOD(extack, "Bridge port configuration for VLAN failed");
+               }
+       }
+
+EXIT:
+       return ret;
+}
+
+static void mxl862xx_port_bridge_leave(struct dsa_switch *ds, int port,
+                                      struct dsa_bridge br)
+{
+       struct net_device *bridge = br.dev;
+       struct mxl862xx_priv *priv = ds->priv;
+       mxl862xx_bridge_alloc_t br_alloc = { 0 };
+       unsigned int cpu_port = priv->hw_info->cpu_port;
+       int bridgeID = 0;
+       int ret;
+
+       bridgeID = __find_bridgeID(ds, bridge);
+       ret = __update_bridge_conf_port(ds, port, bridge, 0);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(ds->dev,
+                       "%s: bridge port removal failed for port %d, ret %d\n",
+                       __func__, port, ret);
+               goto EXIT;
+       }
+
+       /* If only CPU port mapping found, the bridge should be deleted */
+       if (mxl862xx_bridge_portmap[bridgeID] == BIT(cpu_port + 1)) {
+               br_alloc.bridge_id = priv->port_info[port].bridgeID;
+               ret = mxl862xx_bridge_free(&mxl_dev, &br_alloc);
+               if (ret != MXL862XX_STATUS_OK) {
+                       dev_err(ds->dev,
+                               "%s: bridge free failed for port:%d, BridgeID: %d, ret: %d\n",
+                               __func__, port, br_alloc.bridge_id, ret);
+                       goto EXIT;
+               }
+       }
+
+       ret = __isolate_port(ds, port);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(ds->dev, "%s: Port%d isolation failed with ret:%d\n",
+                       __func__, port, ret);
+       }
+
+       priv->port_info[port].vlan.filtering_mode_locked = false;
+
+EXIT:
+       return;
+}
+
+
+static int mxl862xx_phy_read_mii_bus(struct mii_bus *bus, int addr, int regnum)
+{
+       int ret = mxl862xx_phy_read_mmd(NULL, 0, addr, regnum);
+       return ret;
+}
+
+
+static int mxl862xx_phy_write_mii_bus(struct mii_bus *bus, int addr, int regnum, u16 val)
+{
+       int ret = mxl862xx_phy_write_mmd(NULL, 0, addr, regnum, val);
+       return ret;
+}
+
+static int mxl862xx_phy_read_c45_mii_bus(struct mii_bus *bus, int devnum, int addr, int regnum)
+{
+       int ret = mxl862xx_phy_read_mmd(NULL, devnum, addr, regnum);
+       return ret;
+}
+
+
+static int mxl862xx_phy_write_c45_mii_bus(struct mii_bus *bus, int devnum, int addr, int regnum, u16 val)
+{
+       int ret = mxl862xx_phy_write_mmd(NULL, devnum, addr, regnum, val);
+       return ret;
+}
+
+static int
+mxl862xx_setup_mdio(struct dsa_switch *ds)
+{
+       struct device *dev = ds->dev;
+       struct mii_bus *bus;
+       static int idx;
+       int ret;
+
+       bus = devm_mdiobus_alloc(dev);
+       if (!bus)
+               return -ENOMEM;
+
+#if (KERNEL_VERSION(6, 7, 0) > LINUX_VERSION_CODE)
+       ds->slave_mii_bus = bus;
+#else
+       ds->user_mii_bus = bus;
+#endif
+       bus->name = KBUILD_MODNAME "-mii";
+       snprintf(bus->id, MII_BUS_ID_SIZE, KBUILD_MODNAME "-%d", idx++);
+       bus->read_c45 = mxl862xx_phy_read_c45_mii_bus;
+       bus->write_c45 = mxl862xx_phy_write_c45_mii_bus;
+       bus->read = mxl862xx_phy_read_mii_bus;
+       bus->write = mxl862xx_phy_write_mii_bus;
+       bus->parent = dev;
+       bus->phy_mask = ~ds->phys_mii_mask;
+
+       ret = devm_mdiobus_register(dev, bus);
+       if (ret)
+               dev_err(dev, "failed to register MDIO bus: %d\n", ret);
+
+       return ret;
+}
+
+static int mxl862xx_setup(struct dsa_switch *ds)
+{
+       struct mxl862xx_priv *priv = ds->priv;
+       unsigned int cpu_port = priv->hw_info->cpu_port;
+       int ret = 0;
+       uint8_t i;
+
+       ret = mxl862xx_setup_mdio(ds);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(ds->dev, "%s: mdio setup failed with %d\n", __func__,
+                       ret);
+               goto EXIT;
+       }
+
+       /* Trigger ETHSW_SWRES to re-initiate all previous settings */
+       ret = mxl862xx_mmd_write(&mxl_dev, 1, 0);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(ds->dev, "%s: mmd write failed with %d\n", __func__,
+                       ret);
+               goto EXIT;
+       }
+
+       ret = mxl862xx_mmd_write(&mxl_dev, 0, 0x9907);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(ds->dev, "%s: mmd write failed with %d\n", __func__,
+                       ret);
+               goto EXIT;
+       }
+
+       /* The reset duration could be improved by implementing
+        * register polling at offset 0, 1, 2 for specific values
+        * 0x20, 0x21, 0x22, indicating that booting is completed. */
+       usleep_range(4000000, 6000000);
+
+       priv->port_info[priv->hw_info->cpu_port].tag_protocol =
+               __dt_parse_tag_proto(ds, priv->hw_info->cpu_port);
+
+       if (priv->port_info[priv->hw_info->cpu_port].tag_protocol == DSA_TAG_PROTO_MXL862) {
+               ret = __config_mxl862_tag_proto(ds, cpu_port, true);
+               if (ret != MXL862XX_STATUS_OK) {
+                       dev_err(ds->dev, "%s: DSA tagging protocol setting failed with  %d\n", __func__,
+                               ret);
+                       goto EXIT;
+               }
+       }
+
+       /* For CPU port MAC learning setting depends on the kernel version. */
+       {
+               bool lrn_dis;
+               mxl862xx_bridge_port_config_t br_port_cfg = { 0 };
+
+               br_port_cfg.bridge_port_id = cpu_port + 1;
+               br_port_cfg.mask = MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING;
+               lrn_dis = true;
+               br_port_cfg.src_mac_learning_disable = lrn_dis;
+
+               ret = mxl862xx_bridge_port_config_set(&mxl_dev, &br_port_cfg);
+               if (ret != MXL862XX_STATUS_OK) {
+                       dev_err(ds->dev,
+                               "%s: %s MAC learning for port %d failed with ret=%d\n",
+                               __func__, lrn_dis ? "Disabled" : "Enabled", cpu_port, ret);
+                       goto EXIT;
+               }
+               dev_info(ds->dev, "%s: %s MAC learning for port:%d\n",
+                        __func__, lrn_dis ? "Disabled" : "Enabled", cpu_port);
+       }
+
+       /* Store bridge portmap in the driver cache.
+        * Add CPU port for each bridge. */
+       for (i = 0; i < MAX_BRIDGES; i++)
+               mxl862xx_bridge_portmap[i] = BIT(cpu_port + 1);
+
+       __set_vlan_filters_limits(ds);
+       /* by default set all vlans on cpu port in untagged mode */
+       for (i = 0; i < MAX_VLANS; i++)
+               priv->port_info[cpu_port].vlan.egress_vlan_block_info.vlans[i].untagged = true;
+
+       for (i = 0; i < priv->hw_info->phy_ports; i++) {
+               mxl862xx_register_mod_t register_mod = { 0 };
+
+               /* unblock vlan_filtering change */
+               priv->port_info[i].vlan.filtering_mode_locked = false;
+               priv->port_info[i].isolated = false;
+
+               if (dsa_is_cpu_port(ds, i)) {
+                       dev_info(ds->dev, "%s: cpu port with index :%d\n",
+                                __func__, i);
+                       continue;
+               }
+
+               /* disable datapath */
+               register_mod.reg_addr = MxL862XX_SDMA_PCTRLp(i + 1);
+               register_mod.data = 0;
+               register_mod.mask = MXL862XX_SDMA_PCTRL_EN;
+               ret = mxl862xx_register_mod(&mxl_dev, &register_mod);
+               if (ret != MXL862XX_STATUS_OK) {
+                       dev_err(ds->dev, "%s: Disable Datapath failed:%d\n",
+                               __func__, i);
+               }
+
+               ret = __isolate_port(ds, i);
+               if (ret != MXL862XX_STATUS_OK) {
+                       dev_err(ds->dev,
+                               "%s: Port%d isolation failed with ret:%d\n",
+                               __func__, i, ret);
+//                     goto EXIT;
+               }
+       }
+
+       /* Clear MAC address table */
+       ret = mxl862xx_mac_table_clear(&mxl_dev);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(ds->dev, "%s: MAC table clear failed\n", __func__);
+               goto EXIT;
+       }
+
+EXIT:
+       return ret;
+}
+
+static void mxl862xx_port_stp_state_set(struct dsa_switch *ds, int port,
+                                       u8 state)
+{
+       struct mxl862xx_priv *priv = ds->priv;
+       mxl862xx_stp_port_cfg_t stp_portCfg;
+       int ret;
+
+       if (port < 0 || port >= MAX_PORTS) {
+               dev_err(priv->dev, "invalid port: %d\n", port);
+               return;
+       }
+
+       stp_portCfg.port_id = port + 1;
+
+       switch (state) {
+       case BR_STATE_DISABLED:
+               stp_portCfg.port_state = MXL862XX_STP_PORT_STATE_DISABLE;
+               return;
+       case BR_STATE_BLOCKING:
+       case BR_STATE_LISTENING:
+               stp_portCfg.port_state = MXL862XX_STP_PORT_STATE_BLOCKING;
+               break;
+       case BR_STATE_LEARNING:
+               stp_portCfg.port_state = MXL862XX_STP_PORT_STATE_LEARNING;
+               break;
+       case BR_STATE_FORWARDING:
+               stp_portCfg.port_state = MXL862XX_STP_PORT_STATE_FORWARD;
+               break;
+       default:
+               dev_err(priv->dev, "invalid STP state: %d\n", state);
+               return;
+       }
+
+       ret = mxl862xx_stp_port_cfg_set(&mxl_dev, &stp_portCfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(ds->dev,
+                       "%s: stp configuration failed, port: %d, state: %d\n",
+                       __func__, port, state);
+       }
+
+       /* Since MxL862xx firmware enables MAC learning in some STP states,
+        * we have to disable it for isolated user ports. For CPU port,
+        * this setting depends on the kernel version. */
+       if ((priv->port_info[port].bridge == NULL) || (dsa_is_cpu_port(ds, port))) {
+               mxl862xx_bridge_port_config_t br_port_cfg = { 0 };
+               bool lrn_dis;
+
+               if ((dsa_is_cpu_port(ds, port)))
+                       lrn_dis = true;
+               else
+                       lrn_dis = true;
+
+               br_port_cfg.mask =
+                       MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING;
+               br_port_cfg.bridge_port_id = port + 1;
+               br_port_cfg.src_mac_learning_disable = lrn_dis;
+               ret = mxl862xx_bridge_port_config_set(&mxl_dev, &br_port_cfg);
+               if (ret != MXL862XX_STATUS_OK) {
+                       dev_err(ds->dev,
+                               "%s: %s MAC learning for port %d failed with ret=%d\n",
+                               __func__, lrn_dis ? "Disabled" : "Enabled", port, ret);
+                       return;
+               }
+       }
+}
+
+static void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port,
+                                     struct phylink_config *config)
+{
+       struct mxl862xx_priv *priv = ds->priv;
+
+       if (port >= 0 && port < priv->hw_info->phy_ports) {
+               __set_bit(PHY_INTERFACE_MODE_INTERNAL,
+                         config->supported_interfaces);
+       } else if (port == 8 || port == 9) {
+               __set_bit(PHY_INTERFACE_MODE_USXGMII,
+                         config->supported_interfaces);
+       } else if (port > 9) {
+               __set_bit(PHY_INTERFACE_MODE_NA, config->supported_interfaces);
+       }
+
+       config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 |
+                                  MAC_100 | MAC_1000 |
+                                       MAC_2500FD;
+
+       return;
+}
+
+static void mxl862xx_phylink_mac_config(struct dsa_switch *ds, int port,
+                                       unsigned int mode,
+                                       const struct phylink_link_state *state)
+{
+       switch (state->interface) {
+       case PHY_INTERFACE_MODE_INTERNAL:
+               return;
+       case PHY_INTERFACE_MODE_SGMII:
+               return;
+       case PHY_INTERFACE_MODE_USXGMII:
+               /* Configure the USXGMII */
+               break;
+       default:
+               dev_err(ds->dev, "Unsupported interface: %d\n",
+                       state->interface);
+               return;
+       }
+}
+
+static void mxl862xx_phylink_mac_link_down(struct dsa_switch *ds, int port,
+                                          unsigned int mode,
+                                          phy_interface_t interface)
+{
+       mxl862xx_port_link_cfg_t port_link_cfg = { 0 };
+       int ret;
+
+       if (dsa_is_cpu_port(ds, port))
+               return;
+
+       port_link_cfg.port_id = port + 1;
+
+       port_link_cfg.link_force = true;
+       port_link_cfg.link = MXL862XX_PORT_LINK_DOWN;
+
+       ret = mxl862xx_port_link_cfg_set(&mxl_dev, &port_link_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(ds->dev,
+                       "%s: MAC link port configuration for port %d failed with %d\n",
+                       __func__, port, ret);
+       }
+}
+
+static void mxl862xx_phylink_mac_link_up(struct dsa_switch *ds, int port,
+                                        unsigned int mode,
+                                        phy_interface_t interface,
+                                        struct phy_device *phydev, int speed,
+                                        int duplex, bool tx_pause,
+                                        bool rx_pause)
+{
+       mxl862xx_port_link_cfg_t port_link_cfg = { 0 };
+       mxl862xx_port_cfg_t port_cfg = { 0 };
+       int ret;
+
+       if (dsa_is_cpu_port(ds, port))
+               return;
+
+       port_link_cfg.port_id = port + 1;
+
+       port_link_cfg.link_force = true;
+       port_link_cfg.link = MXL862XX_PORT_LINK_UP;
+
+       port_link_cfg.speed_force = true;
+       switch (speed) {
+       case SPEED_10:
+               port_link_cfg.speed = MXL862XX_PORT_SPEED_10;
+               break;
+       case SPEED_100:
+               port_link_cfg.speed = MXL862XX_PORT_SPEED_100;
+               break;
+       case SPEED_1000:
+               port_link_cfg.speed = MXL862XX_PORT_SPEED_1000;
+               break;
+       case SPEED_2500:
+               port_link_cfg.speed = MXL862XX_PORT_SPEED_2500;
+               break;
+       case SPEED_5000:
+               port_link_cfg.speed = MXL862XX_PORT_SPEED_5000;
+               break;
+       case SPEED_10000:
+               port_link_cfg.speed = MXL862XX_PORT_SPEED_10000;
+               break;
+       default:
+               dev_err(ds->dev,
+                       "%s: Unsupported  MAC link speed %d Mbps on port:%d\n",
+                       __func__, speed, port);
+               return;
+       }
+
+       port_link_cfg.duplex_force = true;
+       switch (duplex) {
+       case DUPLEX_HALF:
+               port_link_cfg.duplex = MXL862XX_DUPLEX_HALF;
+               break;
+       case DUPLEX_FULL:
+               port_link_cfg.duplex = MXL862XX_DUPLEX_FULL;
+               break;
+       default:
+               port_link_cfg.duplex = MXL862XX_DUPLEX_AUTO;
+       }
+
+       ret = mxl862xx_port_link_cfg_set(&mxl_dev, &port_link_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(ds->dev,
+                       "%s: Port link configuration for port %d failed with %d\n",
+                       __func__, port, ret);
+               return;
+       }
+
+       port_cfg.port_id = port + 1;
+       ret = mxl862xx_port_cfg_get(&mxl_dev, &port_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(ds->dev,
+                       "%s: Port configuration read for port %d failed with %d\n",
+                       __func__, port, ret);
+               return;
+       }
+
+       if (tx_pause && rx_pause)
+               port_cfg.flow_ctrl = MXL862XX_FLOW_RXTX;
+       else if (tx_pause)
+               port_cfg.flow_ctrl = MXL862XX_FLOW_TX;
+       else if (rx_pause)
+               port_cfg.flow_ctrl = MXL862XX_FLOW_RX;
+       else
+               port_cfg.flow_ctrl = MXL862XX_FLOW_OFF;
+
+       ret = mxl862xx_port_cfg_set(&mxl_dev, &port_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(ds->dev,
+                       "%s: Port configuration for port %d failed with %d\n",
+                       __func__, port, ret);
+       }
+
+       return;
+}
+
+static void mxl862xx_get_ethtool_stats(struct dsa_switch *ds, int port,
+                                      uint64_t *data)
+{
+       int ret = -EINVAL;
+       uint8_t i = 0;
+       mxl862xx_debug_rmon_port_cnt_t dbg_rmon_port_cnt = { 0 };
+
+       /* RX */
+       dbg_rmon_port_cnt.port_id = port + 1;
+       dbg_rmon_port_cnt.port_type = MXL862XX_RMON_CTP_PORT_RX;
+       ret = mxl862xx_debug_rmon_port_get(&mxl_dev, &dbg_rmon_port_cnt);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(ds->dev,
+                       "%s: Reading RMON RX statistics for port %d failed with %d\n",
+                       __func__, port, ret);
+               return;
+       }
+       data[i++] = dbg_rmon_port_cnt.rx_good_pkts; //0
+       data[i++] = dbg_rmon_port_cnt.rx_unicast_pkts;
+       data[i++] = dbg_rmon_port_cnt.rx_broadcast_pkts;
+       data[i++] = dbg_rmon_port_cnt.rx_multicast_pkts;
+       data[i++] = dbg_rmon_port_cnt.rx_fcserror_pkts;
+       data[i++] = dbg_rmon_port_cnt.rx_under_size_good_pkts;
+       data[i++] = dbg_rmon_port_cnt.rx_oversize_good_pkts;
+       data[i++] = dbg_rmon_port_cnt.rx_under_size_error_pkts;
+       data[i++] = dbg_rmon_port_cnt.rx_oversize_error_pkts;
+       data[i++] = dbg_rmon_port_cnt.rx_filtered_pkts;
+       data[i++] = dbg_rmon_port_cnt.rx64byte_pkts; //10
+       data[i++] = dbg_rmon_port_cnt.rx127byte_pkts;
+       data[i++] = dbg_rmon_port_cnt.rx255byte_pkts;
+       data[i++] = dbg_rmon_port_cnt.rx511byte_pkts;
+       data[i++] = dbg_rmon_port_cnt.rx1023byte_pkts;
+       data[i++] = dbg_rmon_port_cnt.rx_max_byte_pkts;
+       data[i++] = dbg_rmon_port_cnt.rx_dropped_pkts;
+       data[i++] = dbg_rmon_port_cnt.rx_extended_vlan_discard_pkts;
+       data[i++] = dbg_rmon_port_cnt.mtu_exceed_discard_pkts;
+       data[i++] = dbg_rmon_port_cnt.rx_good_bytes;
+       data[i++] = dbg_rmon_port_cnt.rx_bad_bytes; //20
+       data[i++] = dbg_rmon_port_cnt.rx_unicast_pkts_yellow_red;
+       data[i++] = dbg_rmon_port_cnt.rx_broadcast_pkts_yellow_red;
+       data[i++] = dbg_rmon_port_cnt.rx_multicast_pkts_yellow_red;
+       data[i++] = dbg_rmon_port_cnt.rx_good_pkts_yellow_red;
+       data[i++] = dbg_rmon_port_cnt.rx_good_bytes_yellow_red;
+       data[i++] = dbg_rmon_port_cnt.rx_good_pause_pkts;
+       data[i++] = dbg_rmon_port_cnt.rx_align_error_pkts;
+
+       /* TX */
+       memset(&dbg_rmon_port_cnt, 0, sizeof(dbg_rmon_port_cnt));
+       dbg_rmon_port_cnt.port_id = port + 1;
+       dbg_rmon_port_cnt.port_type = MXL862XX_RMON_CTP_PORT_TX;
+       ret = mxl862xx_debug_rmon_port_get(&mxl_dev, &dbg_rmon_port_cnt);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(ds->dev,
+                       "%s: Reading RMON TX statistics for port %d failed with %d\n",
+                       __func__, port, ret);
+               return;
+       }
+       data[i++] = dbg_rmon_port_cnt.tx_good_pkts;
+       data[i++] = dbg_rmon_port_cnt.tx_unicast_pkts;
+       data[i++] = dbg_rmon_port_cnt.tx_broadcast_pkts; //30
+       data[i++] = dbg_rmon_port_cnt.tx_multicast_pkts;
+       data[i++] = dbg_rmon_port_cnt.tx64byte_pkts;
+       data[i++] = dbg_rmon_port_cnt.tx127byte_pkts;
+       data[i++] = dbg_rmon_port_cnt.tx255byte_pkts;
+       data[i++] = dbg_rmon_port_cnt.tx511byte_pkts;
+       data[i++] = dbg_rmon_port_cnt.tx1023byte_pkts;
+       data[i++] = dbg_rmon_port_cnt.tx_max_byte_pkts;
+       data[i++] = dbg_rmon_port_cnt.tx_dropped_pkts;
+       data[i++] = dbg_rmon_port_cnt.tx_acm_dropped_pkts;
+       data[i++] = dbg_rmon_port_cnt.tx_good_bytes; //40
+       data[i++] = dbg_rmon_port_cnt.tx_unicast_pkts_yellow_red;
+       data[i++] = dbg_rmon_port_cnt.tx_broadcast_pkts_yellow_red;
+       data[i++] = dbg_rmon_port_cnt.tx_multicast_pkts_yellow_red;
+       data[i++] = dbg_rmon_port_cnt.tx_good_pkts_yellow_red;
+       data[i++] = dbg_rmon_port_cnt.tx_good_bytes_yellow_red;
+       data[i++] = dbg_rmon_port_cnt.tx_single_coll_count;
+       data[i++] = dbg_rmon_port_cnt.tx_mult_coll_count;
+       data[i++] = dbg_rmon_port_cnt.tx_late_coll_count;
+       data[i++] = dbg_rmon_port_cnt.tx_excess_coll_count;
+       data[i++] = dbg_rmon_port_cnt.tx_coll_count; //50
+       data[i++] = dbg_rmon_port_cnt.tx_pause_count;
+
+       return;
+}
+
+static void mxl862xx_get_strings(struct dsa_switch *ds, int port,
+                                uint32_t stringset, uint8_t *data)
+{
+       uint8_t i;
+
+       if (stringset != ETH_SS_STATS)
+               return;
+       for (i = 0; i < ARRAY_SIZE(mxl862xx_rmon_cnt); i++)
+               strscpy(data + i * ETH_GSTRING_LEN, mxl862xx_rmon_cnt[i].name,
+                       ETH_GSTRING_LEN);
+}
+
+static int mxl862xx_get_sset_count(struct dsa_switch *ds, int port, int sset)
+{
+       if (sset != ETH_SS_STATS)
+               return 0;
+
+       return ARRAY_SIZE(mxl862xx_rmon_cnt);
+}
+
+static void mxl862xx_port_fast_age(struct dsa_switch *ds, int port)
+{
+       int ret = -EINVAL;
+       mxl862xx_mac_table_clear_cond_t param = { 0 };
+
+       param.type = MXL862XX_MAC_CLEAR_PHY_PORT;
+       param.port_id = port + 1;
+
+       ret = mxl862xx_mac_table_clear_cond(&mxl_dev, &param);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(ds->dev,
+                       "%s: Clearing MAC table for port %d failed with %d\n",
+                       __func__, port, ret);
+       }
+}
+
+static int mxl862xx_port_mirror_add(struct dsa_switch *ds, int port,
+                                   struct dsa_mall_mirror_tc_entry *mirror,
+                                   bool ingress, struct netlink_ext_ack *extack)
+{
+       struct mxl862xx_priv *priv = ds->priv;
+       int ret = -EINVAL;
+       mxl862xx_ctp_port_config_t ctp_param = { 0 };
+       mxl862xx_monitor_port_cfg_t mon_param = { 0 };
+
+       if (port < 0 || port >= MAX_PORTS) {
+               dev_err(priv->dev, "invalid port: %d\n", port);
+               goto EXIT;
+       }
+
+       /* first read, then change */
+       ctp_param.logical_port_id = port + 1;
+       ctp_param.mask = MXL862XX_CTP_PORT_CONFIG_MASK_ALL;
+       ret = mxl862xx_ctp_port_config_get(&mxl_dev, &ctp_param);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(ds->dev,
+                       "%s: Enabling monitoring of port %d failed on port config get with %d\n",
+                       __func__, port, ret);
+               goto EXIT;
+       }
+
+       ctp_param.mask = MXL862XX_CTP_PORT_CONFIG_LOOPBACK_AND_MIRROR;
+       if (ingress) {
+               priv->port_info[port].ingress_mirror_enabled = true;
+               ctp_param.ingress_mirror_enable = true;
+       } else {
+               priv->port_info[port].egress_mirror_enabled = true;
+               ctp_param.egress_mirror_enable = true;
+       }
+
+       ret = mxl862xx_ctp_port_config_set(&mxl_dev, &ctp_param);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(ds->dev,
+                       "%s: Enabling monitoring of port %d failed on port config set with %d\n",
+                       __func__, port, ret);
+               goto EXIT;
+       }
+
+       mon_param.port_id = mirror->to_local_port + 1;
+       ret = mxl862xx_monitor_port_cfg_set(&mxl_dev, &mon_param);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(ds->dev, "%s: Setting monitor port %d failed with %d\n",
+                       __func__, mon_param.port_id, ret);
+               goto EXIT;
+       }
+
+EXIT:
+       return ret;
+}
+
+static void mxl862xx_port_mirror_del(struct dsa_switch *ds, int port,
+                                    struct dsa_mall_mirror_tc_entry *mirror)
+{
+       int ret = -EINVAL;
+       uint8_t i;
+       struct mxl862xx_priv *priv = ds->priv;
+       uint8_t phy_ports = priv->hw_info->phy_ports;
+       mxl862xx_ctp_port_config_t ctp_param = { 0 };
+
+       if (port < 0 || port >= MAX_PORTS) {
+               dev_err(priv->dev, "invalid port: %d\n", port);
+               return;
+       }
+
+       ctp_param.logical_port_id = port + 1;
+       ctp_param.mask = MXL862XX_CTP_PORT_CONFIG_LOOPBACK_AND_MIRROR;
+       if (mirror->ingress) {
+               priv->port_info[port].ingress_mirror_enabled = false;
+               ctp_param.ingress_mirror_enable = false;
+       } else {
+               priv->port_info[port].egress_mirror_enabled = false;
+               ctp_param.egress_mirror_enable = false;
+       }
+
+       ret = mxl862xx_ctp_port_config_set(&mxl_dev, &ctp_param);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(ds->dev,
+                       "%s: Disabling monitoring of port %d failed with %d\n",
+                       __func__, port, ret);
+               goto EXIT;
+       }
+
+       for (i = 0; i < phy_ports; i++) {
+               /* some ports are still mirrored, keep the monitor port configured */
+               if (priv->port_info[i].egress_mirror_enabled ||
+                   priv->port_info[i].egress_mirror_enabled)
+                       break;
+               /* checked all and no port is being mirrored - release the monitor port */
+               if (i == phy_ports - 1) {
+                       mxl862xx_monitor_port_cfg_t mon_param = { 0 };
+
+                       ret = mxl862xx_monitor_port_cfg_set(&mxl_dev,
+                                                           &mon_param);
+                       if (ret != MXL862XX_STATUS_OK) {
+                               dev_err(ds->dev,
+                                       "%s: Releasing monitor port %d failed with %d\n",
+                                       __func__, mon_param.port_id, ret);
+                               goto EXIT;
+                       }
+               }
+       }
+
+EXIT:
+       return;
+}
+
+static int
+__get_vlan_vid_filters_idx(struct mxl862xx_priv *priv, uint8_t port, bool ingress,
+               uint16_t vid, int *f_0, int *f_1, uint16_t *vlan_idx)
+{
+       int ret = -EINVAL;
+       int x, i = 0;
+       /* negative values if not found */
+       int filter_0 = -1;
+       int filter_1 = -1;
+       struct mxl862xx_extended_vlan_block_info *block_info;
+
+       if (ingress)
+               block_info = &(priv->port_info[port].vlan.ingress_vlan_block_info);
+       else
+               block_info = &(priv->port_info[port].vlan.egress_vlan_block_info);
+
+       /* Check if there's active entry for the requested VLAN. If found, overwrite it. */
+       if (filter_0 < 0 && filter_1 < 0) {
+               for (i = 0; i < MAX_VLANS; i++) {
+                       if (block_info->vlans[i].vid == vid) {
+                               filter_0 = block_info->vlans[i].filters_idx[0];
+                               filter_1 = block_info->vlans[i].filters_idx[1];
+                               ret = 0;
+                               break;
+                       }
+               }
+       }
+
+       /* If there are no matching active VLAN entries, check in recycled */
+       if (filter_0 < 0 && filter_1 < 0) {
+       /* check if there are recycled filter entries for use */
+               for (x = 0; x < MAX_VLANS; x++) {
+                       if (block_info->filter_entries_recycled[x].valid) {
+                               filter_0 = block_info->filter_entries_recycled[x].filters_idx[0];
+                               filter_1 = block_info->filter_entries_recycled[x].filters_idx[1];
+                               /* remove filter entries from recycled inventory */
+                               block_info->filter_entries_recycled[x].valid = false;
+                               ret = 0;
+                               break;
+                       }
+               }
+
+               /* find empty slot for storing ID's of vlan filtering rules */
+               for (i = 0; i < MAX_VLANS; i++) {
+                       if (!(block_info->vlans[i].used)) {
+                               ret = 0;
+                               break;
+                       }
+                       if (i == priv->max_vlans - 1) {
+                               ret = -ENOSPC;
+                               dev_err(priv->dev,
+                                       "%s: Port:%d reached max number of defined VLAN's: %d\n",
+                                       __func__, port, priv->max_vlans);
+                               goto EXIT;
+                       }
+               }
+       }
+
+       if (f_0 != NULL)
+               *f_0 = filter_0;
+       if (f_1 != NULL)
+               *f_1 = filter_1;
+       if (vlan_idx != NULL)
+               *vlan_idx = i;
+
+EXIT:
+       return ret;
+}
+
+
+static int
+__prepare_vlan_egress_filters_off_sp_tag_no_vid(struct dsa_switch *ds, uint8_t port)
+{
+       int ret = -EINVAL;
+       struct mxl862xx_priv *priv = ds->priv;
+
+       mxl862xx_extendedvlan_config_t vlan_cfg = { 0 };
+       /* Allocate new block if needed */
+       if (!(priv->port_info[port].vlan.egress_vlan_block_info.allocated)) {
+               mxl862xx_extendedvlan_alloc_t vlan_alloc = { 0 };
+               /* Reserve fixed number of entries per port and direction */
+               vlan_alloc.number_of_entries =
+                       priv->port_info[port]
+                               .vlan.egress_vlan_block_info.filters_max;
+               ret = mxl862xx_extended_vlan_alloc(&mxl_dev, &vlan_alloc);
+               if (ret != MXL862XX_STATUS_OK)
+                       goto EXIT;
+
+               priv->port_info[port].vlan.egress_vlan_block_info.allocated =
+                       true;
+               priv->port_info[port].vlan.egress_vlan_block_info.block_id =
+                       vlan_alloc.extended_vlan_block_id;
+       }
+
+       // Static entry :  Outer and iner tag.
+       // Remove outer tag  one as it must be sp_tag. Transparent for inner tag.
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id =
+               priv->port_info[port].vlan.egress_vlan_block_info.block_id;
+       vlan_cfg.entry_index = priv->port_info[port].vlan.egress_vlan_block_info.filters_max - 2;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+       /* remove  sp tag */
+       vlan_cfg.treatment.remove_tag = MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG;
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(priv->dev,
+                       "%s: Port:%d failed to add entry:%d for egress extended VLAN block ID:%d\n",
+                       __func__, port, vlan_cfg.entry_index,
+                       vlan_cfg.extended_vlan_block_id);
+               goto EXIT;
+       }
+
+       priv->port_info[port].vlan.egress_vlan_block_info.final_filters_idx = vlan_cfg.entry_index;
+
+       // Last entry :  Only outer tag. Remove it as it must be sp_tag
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id =
+               priv->port_info[port].vlan.egress_vlan_block_info.block_id;
+       vlan_cfg.entry_index = priv->port_info[port].vlan.egress_vlan_block_info.filters_max - 1;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+       /* remove  sp tag */
+       vlan_cfg.treatment.remove_tag = MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG;
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(priv->dev,
+                       "%s: Port:%d failed to add entry:%d for egress extended VLAN block ID:%d\n",
+                       __func__, port, vlan_cfg.entry_index,
+                       vlan_cfg.extended_vlan_block_id);
+               goto EXIT;
+       }
+
+       priv->port_info[port].vlan.egress_vlan_block_info.final_filters_idx = vlan_cfg.entry_index;
+
+EXIT:
+       return ret;
+}
+
+
+static int
+__prepare_vlan_egress_filters_off_sp_tag(struct dsa_switch *ds, uint8_t port, uint16_t vid, bool untagged)
+{
+       int ret = -EINVAL;
+       uint16_t idx = 0;
+       /* negative values if not found */
+       int filter_0 = -1;
+       int filter_1 = -1;
+       struct mxl862xx_priv *priv = ds->priv;
+
+       mxl862xx_extendedvlan_config_t vlan_cfg = { 0 };
+       /* Allocate new block if needed */
+       if (!(priv->port_info[port].vlan.egress_vlan_block_info.allocated)) {
+               mxl862xx_extendedvlan_alloc_t vlan_alloc = { 0 };
+               /* Reserve fixed number of entries per port and direction */
+               vlan_alloc.number_of_entries =
+                       priv->port_info[port]
+                               .vlan.egress_vlan_block_info.filters_max;
+               ret = mxl862xx_extended_vlan_alloc(&mxl_dev, &vlan_alloc);
+               if (ret != MXL862XX_STATUS_OK)
+                       goto EXIT;
+
+               priv->port_info[port].vlan.egress_vlan_block_info.allocated =
+                       true;
+               priv->port_info[port].vlan.egress_vlan_block_info.block_id =
+                       vlan_alloc.extended_vlan_block_id;
+       }
+
+       /* VID specific entries must be processed before the final entries,
+        * so putting them at the beginnig of the block */
+
+       ret = __get_vlan_vid_filters_idx(priv, port, false, vid, &filter_0, &filter_1, &idx);
+       dev_dbg(priv->dev, "%s: Port:%d  vid:%d f_0:%d f_1:%d idx:%d\n", __func__, port, vid, filter_0, filter_1, idx);
+       if (ret != MXL862XX_STATUS_OK)
+               goto EXIT;
+
+       // Entry 0 :  Outer and Inner tags are present. Inner tag matching vid.
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id =
+               priv->port_info[port].vlan.egress_vlan_block_info.block_id;
+       /* if found recycled entry reuse it, otherwise create new one */
+       vlan_cfg.entry_index =
+               filter_0 >= 0 ?
+                       filter_0 :
+                       priv->port_info[port]
+                               .vlan.egress_vlan_block_info.vid_filters_idx++;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.filter.inner_vlan.vid_enable = true;
+       vlan_cfg.filter.inner_vlan.vid_val = vid;
+
+       if (untagged) {
+               /* remove both sp_tag(outer) and vid (inner) */
+               vlan_cfg.treatment.remove_tag = MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_2_TAG;
+       } else {
+               /* remove only sp tag */
+               vlan_cfg.treatment.remove_tag = MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG;
+       }
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(priv->dev,
+                       "%s: Port:%d failed to add entry:%d for egress extended VLAN block ID:%d\n",
+                       __func__, port, vlan_cfg.entry_index,
+                       vlan_cfg.extended_vlan_block_id);
+               goto EXIT;
+       }
+
+       /* store VLAN filtering rules ID's (for VLAN delete, if needed) */
+       priv->port_info[port]
+               .vlan.egress_vlan_block_info.vlans[idx]
+               .filters_idx[0] = vlan_cfg.entry_index;
+
+       priv->port_info[port]
+               .vlan.egress_vlan_block_info.vlans[idx]
+               .filters_idx[1] = IDX_INVAL;
+
+       // Static entry :  Outer and iner tag, not matching vid. Remove outer tag  one as it must be sp_tag
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id =
+               priv->port_info[port].vlan.egress_vlan_block_info.block_id;
+       vlan_cfg.entry_index = priv->port_info[port].vlan.egress_vlan_block_info.filters_max - 2;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+       /* remove  sp tag */
+       vlan_cfg.treatment.remove_tag = MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG;
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(priv->dev,
+                       "%s: Port:%d failed to add entry:%d for egress extended VLAN block ID:%d\n",
+                       __func__, port, vlan_cfg.entry_index,
+                       vlan_cfg.extended_vlan_block_id);
+               goto EXIT;
+       }
+
+       priv->port_info[port].vlan.egress_vlan_block_info.final_filters_idx = vlan_cfg.entry_index;
+
+       // Last entry :  Only outer tag. Remove it as it must be sp_tag
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id =
+               priv->port_info[port].vlan.egress_vlan_block_info.block_id;
+       vlan_cfg.entry_index = priv->port_info[port].vlan.egress_vlan_block_info.filters_max - 2;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+       /* remove  sp tag */
+       vlan_cfg.treatment.remove_tag = MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG;
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(priv->dev,
+                       "%s: Port:%d failed to add entry:%d for egress extended VLAN block ID:%d\n",
+                       __func__, port, vlan_cfg.entry_index,
+                       vlan_cfg.extended_vlan_block_id);
+               goto EXIT;
+       }
+
+       priv->port_info[port].vlan.egress_vlan_block_info.final_filters_idx = vlan_cfg.entry_index;
+
+       // Last entry :  Only outer tag. Remove it as it must be sp_tag
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id =
+               priv->port_info[port].vlan.egress_vlan_block_info.block_id;
+       vlan_cfg.entry_index = priv->port_info[port].vlan.egress_vlan_block_info.filters_max - 1;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+       /* remove  sp tag */
+       vlan_cfg.treatment.remove_tag = MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG;
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(priv->dev,
+                       "%s: Port:%d failed to add entry:%d for egress extended VLAN block ID:%d\n",
+                       __func__, port, vlan_cfg.entry_index,
+                       vlan_cfg.extended_vlan_block_id);
+               goto EXIT;
+       }
+
+       priv->port_info[port].vlan.egress_vlan_block_info.final_filters_idx = vlan_cfg.entry_index ;
+
+
+       priv->port_info[port].vlan.egress_vlan_block_info.vlans[idx].vid = vid;
+       priv->port_info[port].vlan.egress_vlan_block_info.vlans[idx].used = true;
+       priv->port_info[port].vlan.egress_vlan_block_info.vlans[idx].untagged = untagged;
+
+EXIT:
+       return ret;
+}
+
+static int
+__prepare_vlan_egress_filters_off(struct mxl862xx_priv *priv, uint8_t port, uint16_t vid, bool untagged)
+{
+       int ret = -EINVAL;
+       uint16_t idx = 0;
+       /* negative values if not found */
+       int filter_0 = -1;
+       int filter_1 = -1;
+
+       mxl862xx_extendedvlan_config_t vlan_cfg = { 0 };
+       /* Allocate new block if needed */
+       if (!(priv->port_info[port].vlan.egress_vlan_block_info.allocated)) {
+               mxl862xx_extendedvlan_alloc_t vlan_alloc = { 0 };
+               /* Reserve fixed number of entries per port and direction */
+               vlan_alloc.number_of_entries =
+                       priv->port_info[port]
+                               .vlan.egress_vlan_block_info.filters_max;
+               ret = mxl862xx_extended_vlan_alloc(&mxl_dev, &vlan_alloc);
+               if (ret != MXL862XX_STATUS_OK)
+                       goto EXIT;
+
+               priv->port_info[port].vlan.egress_vlan_block_info.allocated =
+                       true;
+               priv->port_info[port].vlan.egress_vlan_block_info.block_id =
+                       vlan_alloc.extended_vlan_block_id;
+       }
+
+       /* VID specific entries must be processed before the final entries,
+        * so putting them at the beginnig of the block */
+
+       ret = __get_vlan_vid_filters_idx(priv, port, false, vid, &filter_0, &filter_1, &idx);
+       if (ret != MXL862XX_STATUS_OK)
+               goto EXIT;
+
+       // Entry 0 : ACCEPT VLAN tags that are matching  VID. Outer and Inner tags are present
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id =
+               priv->port_info[port].vlan.egress_vlan_block_info.block_id;
+       /* if found recycled entry reuse it, otherwise create new one */
+       vlan_cfg.entry_index =
+               filter_0 >= 0 ?
+                       filter_0 :
+                       priv->port_info[port]
+                               .vlan.egress_vlan_block_info.vid_filters_idx++;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.filter.inner_vlan.vid_enable = true;
+       vlan_cfg.filter.inner_vlan.vid_val = vid;
+       if (untagged)
+               vlan_cfg.treatment.remove_tag =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG;
+       else
+               vlan_cfg.treatment.remove_tag =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG;
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK)
+               goto EXIT;
+
+       /* store VLAN filtering rules ID's (for VLAN delete, if needed) */
+       priv->port_info[port]
+               .vlan.egress_vlan_block_info.vlans[idx]
+               .filters_idx[0] = vlan_cfg.entry_index;
+
+       //       Entry 1 : ACCEPT VLAN tags that are matching PVID or port VID. Only the outer tags are present
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id =
+               priv->port_info[port].vlan.egress_vlan_block_info.block_id;
+       /* if found recycled entry reuse it, otherwise create new one */
+       vlan_cfg.entry_index =
+               filter_1 >= 0 ?
+                       filter_1 :
+                       priv->port_info[port]
+                               .vlan.egress_vlan_block_info.vid_filters_idx++;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+       vlan_cfg.filter.inner_vlan.vid_enable = true;
+       vlan_cfg.filter.inner_vlan.vid_val = vid;
+       if (untagged)
+               vlan_cfg.treatment.remove_tag =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG;
+       else
+               vlan_cfg.treatment.remove_tag =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG;
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK)
+               goto EXIT;
+
+       /* store VLAN filtering rules ID's (for VLAN delete, if needed) */
+       priv->port_info[port]
+               .vlan.egress_vlan_block_info.vlans[idx]
+               .filters_idx[1] = vlan_cfg.entry_index;
+
+       priv->port_info[port].vlan.egress_vlan_block_info.vlans[idx].vid = vid;
+       priv->port_info[port].vlan.egress_vlan_block_info.vlans[idx].used = true;
+       priv->port_info[port].vlan.egress_vlan_block_info.vlans[idx].untagged = untagged;
+
+
+EXIT:
+       return ret;
+}
+
+
+static int
+__prepare_vlan_ingress_filters_off_sp_tag_no_vid(struct dsa_switch *ds, uint8_t port)
+{
+       struct mxl862xx_priv *priv = ds->priv;
+       int ret = -EINVAL;
+       struct mxl862xx_extended_vlan_block_info *block_info =
+               &priv->port_info[port].vlan.ingress_vlan_block_info;
+
+       mxl862xx_extendedvlan_config_t vlan_cfg = { 0 };
+       /* Allocate new block if needed */
+       if (!(block_info->allocated)) {
+               mxl862xx_extendedvlan_alloc_t vlan_alloc = { 0 };
+               /* Reserve fixed number of entries per port and direction */
+               vlan_alloc.number_of_entries = block_info->filters_max;
+               ret = mxl862xx_extended_vlan_alloc(&mxl_dev, &vlan_alloc);
+               if (ret != MXL862XX_STATUS_OK)
+                       goto EXIT;
+
+               block_info->allocated = true;
+               block_info->block_id = vlan_alloc.extended_vlan_block_id;
+       }
+
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id = block_info->block_id;
+
+       //Static rules. No tags, add SP tag
+       vlan_cfg.entry_index = block_info->filters_max - 3;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+       vlan_cfg.treatment.add_outer_vlan = true;
+       vlan_cfg.treatment.outer_vlan.vid_mode =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL;
+       vlan_cfg.treatment.outer_vlan.tpid =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q;
+       vlan_cfg.treatment.outer_vlan.priority_mode =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+       vlan_cfg.treatment.outer_vlan.priority_val = 0;
+       vlan_cfg.treatment.outer_vlan.dei =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0;
+       vlan_cfg.treatment.outer_vlan.vid_val = dsa_8021q_rx_vid(ds, port);
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(priv->dev,
+                       "%s: Port:%d failed to add entry:%d for ingress extended VLAN block ID:%d\n",
+                       __func__, port, vlan_cfg.entry_index, vlan_cfg.extended_vlan_block_id);
+               goto EXIT;
+       }
+
+       block_info->final_filters_idx = vlan_cfg.entry_index;
+
+       // Static rules
+       // Single tag. Use transparent mode. Add sp tag
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id = block_info->block_id;
+       vlan_cfg.entry_index = block_info->filters_max - 2;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+       vlan_cfg.treatment.add_outer_vlan = true;
+       vlan_cfg.treatment.outer_vlan.vid_mode =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL;
+       vlan_cfg.treatment.outer_vlan.tpid =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q;
+       vlan_cfg.treatment.outer_vlan.priority_mode =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+       vlan_cfg.treatment.outer_vlan.priority_val = 0;
+       vlan_cfg.treatment.outer_vlan.dei =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0;
+       vlan_cfg.treatment.outer_vlan.vid_val = dsa_8021q_rx_vid(ds, port);
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(priv->dev,
+                       "%s: Port:%d failed to add entry:%d for ingress extended VLAN block ID:%d\n",
+                       __func__, port, vlan_cfg.entry_index, vlan_cfg.extended_vlan_block_id);
+               goto EXIT;
+       }
+
+       block_info->final_filters_idx = vlan_cfg.entry_index;
+
+       // Two tags. Use transparent mode. Do not apply vid as this is tagged pkt
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id = block_info->block_id;
+       vlan_cfg.entry_index = block_info->filters_max - 1;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+       vlan_cfg.treatment.add_outer_vlan = true;
+       vlan_cfg.treatment.outer_vlan.vid_mode =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL;
+       vlan_cfg.treatment.outer_vlan.tpid =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q;
+       vlan_cfg.treatment.outer_vlan.priority_mode =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+       vlan_cfg.treatment.outer_vlan.priority_val = 0;
+       vlan_cfg.treatment.outer_vlan.dei =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0;
+       vlan_cfg.treatment.outer_vlan.vid_val = dsa_8021q_rx_vid(ds, port);
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(priv->dev,
+                       "%s: Port:%d failed to add entry:%d for ingress extended VLAN block ID:%d\n",
+                       __func__, port, vlan_cfg.entry_index, vlan_cfg.extended_vlan_block_id);
+               goto EXIT;
+       }
+
+       block_info->final_filters_idx = vlan_cfg.entry_index;
+
+EXIT:
+       return ret;
+}
+
+static int
+__prepare_vlan_ingress_filters_off_sp_tag(struct dsa_switch *ds, uint8_t port, uint16_t vid)
+{
+       struct mxl862xx_priv *priv = ds->priv;
+       int ret = -EINVAL;
+       //there's possible only one rule for single pvid, so it always uses idx 0
+       uint16_t idx = 0;
+       struct mxl862xx_extended_vlan_block_info *block_info =
+               &priv->port_info[port].vlan.ingress_vlan_block_info;
+
+       mxl862xx_extendedvlan_config_t vlan_cfg = { 0 };
+       /* Allocate new block if needed */
+       if (!(block_info->allocated)) {
+               mxl862xx_extendedvlan_alloc_t vlan_alloc = { 0 };
+               /* Reserve fixed number of entries per port and direction */
+               vlan_alloc.number_of_entries = block_info->filters_max;
+               ret = mxl862xx_extended_vlan_alloc(&mxl_dev, &vlan_alloc);
+               if (ret != MXL862XX_STATUS_OK)
+                       goto EXIT;
+
+               block_info->allocated = true;
+               block_info->block_id = vlan_alloc.extended_vlan_block_id;
+       }
+
+       /*  If port has pvid then add vid dependand dynamic rule.
+        *  It's done that way because it's required for proper handling of
+        *  vlan delete scenario. If no pvid configured, create 'static' rule */
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id = block_info->block_id;
+
+       // vid dynamic rule
+       if (priv->port_info[port].vlan.pvid) {
+               /* As there's only one  pvid  per port possible, always overwrite the rule at position 0 */
+               vlan_cfg.entry_index = 0;
+               vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+               vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+               vlan_cfg.treatment.add_outer_vlan = true;
+               vlan_cfg.treatment.outer_vlan.vid_mode =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL;
+               vlan_cfg.treatment.outer_vlan.tpid =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q;
+               vlan_cfg.treatment.outer_vlan.priority_mode =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+               vlan_cfg.treatment.outer_vlan.priority_val = 0;
+               vlan_cfg.treatment.outer_vlan.dei =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0;
+               vlan_cfg.treatment.outer_vlan.vid_val = dsa_8021q_rx_vid(ds, port);
+               vlan_cfg.treatment.add_inner_vlan = true;
+               vlan_cfg.treatment.inner_vlan.vid_mode =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL;
+               vlan_cfg.treatment.inner_vlan.vid_val =
+                       priv->port_info[port].vlan.pvid;
+               vlan_cfg.treatment.inner_vlan.tpid =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q;
+               vlan_cfg.treatment.inner_vlan.priority_mode =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+               vlan_cfg.treatment.inner_vlan.priority_val = 0;
+               vlan_cfg.treatment.inner_vlan.dei =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0;
+
+               ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+               if (ret != MXL862XX_STATUS_OK) {
+                       dev_err(priv->dev,
+                               "%s: Port:%d failed to add entry:%d for ingress extended VLAN block ID:%d\n",
+                               __func__, port, vlan_cfg.entry_index,
+                               vlan_cfg.extended_vlan_block_id);
+                       goto EXIT;
+               }
+
+               block_info->vlans[idx].filters_idx[0] = vlan_cfg.entry_index;
+               block_info->vlans[idx].filters_idx[1] = IDX_INVAL;
+
+               block_info->vlans[idx].vid = vid;
+               block_info->vlans[idx].used = true;
+
+       }
+       // no pvid, static rule
+       else {
+               // deactivate possible dynamic rule if there's no pvid
+               if (block_info->vlans[idx].vid) {
+                       ret = __deactivate_vlan_filter_entry(block_info->block_id, block_info->vlans[idx].filters_idx[0]);
+                       if (ret != MXL862XX_STATUS_OK)
+                               goto EXIT;
+                       block_info->vlans[idx].vid = 0;
+                       block_info->vlans[idx].used = false;
+               }
+
+               vlan_cfg.entry_index = block_info->filters_max - 3;
+               vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+               vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+               vlan_cfg.treatment.add_outer_vlan = true;
+               vlan_cfg.treatment.outer_vlan.vid_mode =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL;
+               vlan_cfg.treatment.outer_vlan.tpid =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q;
+               vlan_cfg.treatment.outer_vlan.priority_mode =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+               vlan_cfg.treatment.outer_vlan.priority_val = 0;
+               vlan_cfg.treatment.outer_vlan.dei =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0;
+               vlan_cfg.treatment.outer_vlan.vid_val = dsa_8021q_rx_vid(ds, port);
+
+               ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+               if (ret != MXL862XX_STATUS_OK) {
+                       dev_err(priv->dev,
+                               "%s: Port:%d failed to add entry:%d for ingress extended VLAN block ID:%d\n",
+                               __func__, port, vlan_cfg.entry_index, vlan_cfg.extended_vlan_block_id);
+                       goto EXIT;
+               }
+
+               block_info->final_filters_idx = vlan_cfg.entry_index;
+       }
+
+       // Static rules
+       // Single tag. Use transparent mode. Do not apply PVID as this is the tagged traffic
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id = block_info->block_id;
+       vlan_cfg.entry_index = block_info->filters_max - 2;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+       vlan_cfg.treatment.add_outer_vlan = true;
+       vlan_cfg.treatment.outer_vlan.vid_mode =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL;
+       vlan_cfg.treatment.outer_vlan.tpid =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q;
+       vlan_cfg.treatment.outer_vlan.priority_mode =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+       vlan_cfg.treatment.outer_vlan.priority_val = 0;
+       vlan_cfg.treatment.outer_vlan.dei =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0;
+       vlan_cfg.treatment.outer_vlan.vid_val = dsa_8021q_rx_vid(ds, port);
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(priv->dev,
+                       "%s: Port:%d failed to add entry:%d for ingress extended VLAN block ID:%d\n",
+                       __func__, port, vlan_cfg.entry_index, vlan_cfg.extended_vlan_block_id);
+               goto EXIT;
+       }
+
+       block_info->final_filters_idx = vlan_cfg.entry_index;
+
+       // Two tags. Use transparent mode. Do not apply vid as this is tagged pkt
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id = block_info->block_id;
+       vlan_cfg.entry_index = block_info->filters_max - 1;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+       vlan_cfg.treatment.add_outer_vlan = true;
+       vlan_cfg.treatment.outer_vlan.vid_mode =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL;
+       vlan_cfg.treatment.outer_vlan.tpid =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q;
+       vlan_cfg.treatment.outer_vlan.priority_mode =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+       vlan_cfg.treatment.outer_vlan.priority_val = 0;
+       vlan_cfg.treatment.outer_vlan.dei =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0;
+       vlan_cfg.treatment.outer_vlan.vid_val = dsa_8021q_rx_vid(ds, port);
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(priv->dev,
+                       "%s: Port:%d failed to add entry:%d for ingress extended VLAN block ID:%d\n",
+                       __func__, port, vlan_cfg.entry_index, vlan_cfg.extended_vlan_block_id);
+               goto EXIT;
+       }
+
+       block_info->final_filters_idx = vlan_cfg.entry_index;
+
+EXIT:
+       return ret;
+}
+
+static int
+__prepare_vlan_ingress_filters_off(struct mxl862xx_priv *priv, uint8_t port, uint16_t vid)
+{
+       int ret = -EINVAL;
+       uint16_t idx = 0;
+
+       mxl862xx_extendedvlan_config_t vlan_cfg = { 0 };
+       /* Allocate new block if needed */
+       if (!(priv->port_info[port].vlan.ingress_vlan_block_info.allocated)) {
+               mxl862xx_extendedvlan_alloc_t vlan_alloc = { 0 };
+               /* Reserve fixed number of entries per port and direction */
+               vlan_alloc.number_of_entries =
+                       priv->port_info[port]
+                               .vlan.ingress_vlan_block_info.filters_max;
+               ret = mxl862xx_extended_vlan_alloc(&mxl_dev, &vlan_alloc);
+               if (ret != MXL862XX_STATUS_OK)
+                       goto EXIT;
+
+               priv->port_info[port].vlan.ingress_vlan_block_info.allocated =
+                       true;
+               priv->port_info[port].vlan.ingress_vlan_block_info.block_id =
+                       vlan_alloc.extended_vlan_block_id;
+       }
+
+       // Entry 4  untagged pkts. If there's PVID accept and add PVID tag
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id =
+               priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+       /* for cpu port this entry is fixed and always put at the end of the block */
+       if (port == priv->hw_info->cpu_port)
+               vlan_cfg.entry_index = priv->port_info[port].vlan.ingress_vlan_block_info.filters_max - 1;
+       else {
+               vlan_cfg.entry_index =
+                       priv->port_info[port]
+                               .vlan.ingress_vlan_block_info.final_filters_idx--;
+       }
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+
+       if (priv->port_info[port].vlan.pvid) {
+               vlan_cfg.treatment.add_outer_vlan = true;
+               vlan_cfg.treatment.outer_vlan.vid_mode =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL;
+               vlan_cfg.treatment.outer_vlan.vid_val =
+                       priv->port_info[port].vlan.pvid;
+               vlan_cfg.treatment.outer_vlan.tpid =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q;
+               vlan_cfg.treatment.outer_vlan.priority_mode =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+               vlan_cfg.treatment.outer_vlan.priority_val = 0;
+               vlan_cfg.treatment.outer_vlan.dei =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0;
+       }
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK)
+               goto EXIT;
+
+       ret = __get_vlan_vid_filters_idx(priv, port, true, vid, NULL, NULL, &idx);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(priv->dev,
+                       "%s: Port:%d couldn't get idx for VID:%d and  block ID:%d\n",
+                       __func__, port, vid, vlan_cfg.extended_vlan_block_id);
+               goto EXIT;
+       }
+
+       priv->port_info[port].vlan.ingress_vlan_block_info.vlans[idx].vid = vid;
+       priv->port_info[port].vlan.ingress_vlan_block_info.vlans[idx].used = true;
+
+EXIT:
+       return ret;
+}
+
+static int mxl862xx_port_vlan_filtering(struct dsa_switch *ds, int port,
+                                       bool vlan_filtering,
+                                       struct netlink_ext_ack *extack)
+{
+       int ret = 0;
+       struct mxl862xx_priv *priv = ds->priv;
+       struct dsa_port *dsa_port = dsa_to_port(ds, port);
+       struct net_device *bridge = dsa_port_bridge_dev_get(dsa_port);
+
+       /* Prevent dynamic setting of the vlan_filtering. */
+       if (bridge && priv->port_info[port].vlan.filtering_mode_locked) {
+               ret = -ENOTSUPP;
+               dev_err(ds->dev, "%s: Change of vlan_filtering mode is not allowed while port:%d is joined to a bridge\n",
+                               __func__, port);
+                               NL_SET_ERR_MSG_MOD(extack, "Change of vlan_filtering mode is not allowedwhile port is joind to a bridge.");
+
+       } else {
+               priv->port_info[port].vlan.filtering = vlan_filtering;
+               /* Do not lock if port is isolated. */
+               if (!priv->port_info[port].isolated)
+                       priv->port_info[port].vlan.filtering_mode_locked = true;
+       }
+
+       return ret;
+}
+
+static int
+__prepare_vlan_egress_filters(struct dsa_switch *ds, uint8_t port, uint16_t vid, bool untagged)
+{
+       int ret = -EINVAL;
+       uint16_t idx = 0;
+       /* negative values if not found */
+       int filter_0 = -1;
+       int filter_1 = -1;
+       struct mxl862xx_priv *priv = ds->priv;
+
+       mxl862xx_extendedvlan_config_t vlan_cfg = { 0 };
+       struct mxl862xx_extended_vlan_block_info *block_info =
+               &priv->port_info[port].vlan.egress_vlan_block_info;
+
+       /* Allocate new block if needed */
+       if (!(block_info->allocated)) {
+               mxl862xx_extendedvlan_alloc_t vlan_alloc = { 0 };
+               /* Reserve fixed number of entries per port and direction */
+               vlan_alloc.number_of_entries = block_info->filters_max;
+               ret = mxl862xx_extended_vlan_alloc(&mxl_dev, &vlan_alloc);
+               if (ret != MXL862XX_STATUS_OK)
+                       goto EXIT;
+
+               block_info->allocated = true;
+               block_info->block_id = vlan_alloc.extended_vlan_block_id;
+       }
+
+       /* First populate the block with set of rules which should be executed finally after
+        * VID specific filtering. The final rules (not related to VID) are placed on the end of the block. The number of
+        * rules is fixed  per port. Order of execution  is important. To avoid static reservations they are
+        * stored in reversed order starting from the end of the block */
+
+       //Entry 4: no outer/inner tag, no PVID  DISCARD
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id = block_info->block_id;
+       vlan_cfg.entry_index = block_info->final_filters_idx--;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+       vlan_cfg.treatment.remove_tag =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM;
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK)
+               goto EXIT;
+
+       //Entry 3: Only Outer tag present. Discard if VID is not matching the previous rules
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id = block_info->block_id;
+       vlan_cfg.entry_index = block_info->final_filters_idx--;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+       vlan_cfg.treatment.remove_tag =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM;
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK)
+               goto EXIT;
+
+       //Entry 2: Outer and Inner tags are present. Discard if VID is not matching the previous rules
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id = block_info->block_id;
+       vlan_cfg.entry_index = block_info->final_filters_idx--;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.treatment.remove_tag =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM;
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK)
+               goto EXIT;
+
+       /* VID specific entries must be processed before the final entries,
+        * so putting them at the beginnig of the block */
+       ret = __get_vlan_vid_filters_idx(priv, port, false, vid, &filter_0, &filter_1, &idx);
+       if (ret != MXL862XX_STATUS_OK)
+               goto EXIT;
+
+       // Entry 0 : ACCEPT VLAN tags that are matching  VID. Outer and Inner tags are present
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id = block_info->block_id;
+       /* if found recycled entry reuse it, otherwise create new one */
+       vlan_cfg.entry_index =
+               filter_0 >= 0 ? filter_0 : block_info->vid_filters_idx++;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.filter.outer_vlan.vid_enable = true;
+       vlan_cfg.filter.outer_vlan.vid_val = vid;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+
+       if (untagged)
+               vlan_cfg.treatment.remove_tag =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG;
+       else
+               vlan_cfg.treatment.remove_tag =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG;
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK)
+               goto EXIT;
+
+       /* store VLAN filtering rules ID's (for VLAN delete, if needed) */
+       block_info->vlans[idx].filters_idx[0] = vlan_cfg.entry_index;
+
+       //       Entry 1 : ACCEPT VLAN tags that are matching PVID or port VID. Only the outer tags are present
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id = block_info->block_id;
+       /* if found recycled entry reuse it, otherwise create new one */
+       vlan_cfg.entry_index =
+               filter_1 >= 0 ? filter_1 : block_info->vid_filters_idx++;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.filter.outer_vlan.vid_enable = true;
+       vlan_cfg.filter.outer_vlan.vid_val = vid;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+       if (untagged)
+               vlan_cfg.treatment.remove_tag =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG;
+       else
+               vlan_cfg.treatment.remove_tag =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG;
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK)
+               goto EXIT;
+
+       /* store VLAN filtering rules ID's (for VLAN delete, if needed) */
+       block_info->vlans[idx].filters_idx[1] = vlan_cfg.entry_index;
+
+       block_info->vlans[idx].vid = vid;
+       block_info->vlans[idx].used = true;
+       block_info->vlans[idx].untagged = untagged;
+
+EXIT:
+       return ret;
+}
+
+static int
+__prepare_vlan_egress_filters_sp_tag(struct dsa_switch *ds, uint8_t port, uint16_t vid, bool untagged)
+{
+       int ret = -EINVAL;
+       uint16_t idx = 0;
+       /* negative values if not found */
+       int filter_0 = IDX_INVAL;
+       int filter_1 = IDX_INVAL;
+       struct mxl862xx_priv *priv = ds->priv;
+
+       mxl862xx_extendedvlan_config_t vlan_cfg = { 0 };
+
+       /* Allocate new block if needed */
+       if (!(priv->port_info[port].vlan.egress_vlan_block_info.allocated)) {
+               mxl862xx_extendedvlan_alloc_t vlan_alloc = { 0 };
+               /* Reserve fixed number of entries per port and direction */
+               vlan_alloc.number_of_entries =
+                       priv->port_info[port]
+                               .vlan.egress_vlan_block_info.filters_max;
+               ret = mxl862xx_extended_vlan_alloc(&mxl_dev, &vlan_alloc);
+               if (ret != MXL862XX_STATUS_OK) {
+                       dev_err(priv->dev,
+                               "%s: Extended VLAN allocation for port%d egress filtering failed with %d\n",
+                               __func__, port, ret);
+                       goto EXIT;
+               }
+
+               priv->port_info[port].vlan.egress_vlan_block_info.allocated =
+                       true;
+               priv->port_info[port].vlan.egress_vlan_block_info.block_id =
+                       vlan_alloc.extended_vlan_block_id;
+       }
+
+       /* First populate the fixed block of rules which should be executed finally after
+        * VID specific filtering. The final rules (not related to VID)
+        * are placed at the end of the block. */
+
+       //Entry 4: only outer tag (SP tag), no PVID  DISCARD
+       if (untagged) {
+               memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+               vlan_cfg.extended_vlan_block_id =
+                       priv->port_info[port].vlan.egress_vlan_block_info.block_id;
+          vlan_cfg.entry_index = priv->port_info[port].vlan.egress_vlan_block_info.filters_max - 1;
+               vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+               vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+               vlan_cfg.treatment.remove_tag =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM;
+
+               ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+               if (ret != MXL862XX_STATUS_OK) {
+                       dev_err(priv->dev,
+                               "%s: Port:%d failed to add entry:%d for egress extended VLAN block ID:%d\n",
+                               __func__, port, vlan_cfg.entry_index,
+                               vlan_cfg.extended_vlan_block_id);
+                       goto EXIT;
+               }
+
+               priv->port_info[port].vlan.egress_vlan_block_info.final_filters_idx =
+                       vlan_cfg.entry_index;
+       }
+
+       //Entry 3: there is any other inner tag -> discard upstream traffic
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id =
+               priv->port_info[port].vlan.egress_vlan_block_info.block_id;
+       vlan_cfg.entry_index = priv->port_info[port].vlan.egress_vlan_block_info.filters_max - 2;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.treatment.remove_tag = MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM;
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(priv->dev,
+                       "%s: Port:%d failed to add entry:%d for egress extended VLAN block ID:%d\n",
+                       __func__, port, vlan_cfg.entry_index,
+                       vlan_cfg.extended_vlan_block_id);
+               goto EXIT;
+       }
+
+       priv->port_info[port].vlan.egress_vlan_block_info.final_filters_idx =
+               vlan_cfg.entry_index;
+
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id =
+               priv->port_info[port].vlan.egress_vlan_block_info.block_id;
+       vlan_cfg.entry_index = priv->port_info[port].vlan.egress_vlan_block_info.filters_max - 3;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+       vlan_cfg.treatment.remove_tag =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG;
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(priv->dev,
+                       "%s: Port:%d failed to add entry:%d for egress extended VLAN block ID:%d\n",
+                       __func__, port, vlan_cfg.entry_index,
+                       vlan_cfg.extended_vlan_block_id);
+               goto EXIT;
+       }
+
+       priv->port_info[port].vlan.egress_vlan_block_info.final_filters_idx = vlan_cfg.entry_index;
+
+       /* VID specific entries must be processed before the final entries,
+        * so putting them at the beginnig of the block */
+       ret = __get_vlan_vid_filters_idx(priv, port, false, vid, &filter_0, &filter_1, &idx);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(priv->dev,
+                       "%s: Port:%d couldn't get idx for VID specific filters for VID:%d and  block ID:%d\n",
+                       __func__, port, vid, vlan_cfg.extended_vlan_block_id);
+               goto EXIT;
+       }
+
+       /* Set of dynamic rules that depend on VID.
+        * The number of rules depends on the number of handled vlans */
+
+       // Entry 0 : ACCEPT VLAN tags that are matching  VID. Outer and Inner tags are present
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id =
+               priv->port_info[port].vlan.egress_vlan_block_info.block_id;
+       /* if found recycled entry reuse it, otherwise create new one */
+       vlan_cfg.entry_index =
+               filter_0 >= 0 ?
+                       filter_0 :
+                       priv->port_info[port]
+                               .vlan.egress_vlan_block_info.vid_filters_idx++;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.filter.inner_vlan.vid_enable = true;
+       vlan_cfg.filter.inner_vlan.vid_val = vid;
+
+       if (untagged) {
+               vlan_cfg.treatment.remove_tag =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_2_TAG;
+       } else {
+               vlan_cfg.treatment.remove_tag =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG;
+       }
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(priv->dev,
+                       "%s: Port:%d failed to add entry:%d for egress extended VLAN block ID:%d\n",
+                       __func__, port, vlan_cfg.entry_index,
+                       vlan_cfg.extended_vlan_block_id);
+               goto EXIT;
+       }
+
+       /* store VLAN filtering rules ID's (for VLAN delete, if needed) */
+       priv->port_info[port]
+               .vlan.egress_vlan_block_info.vlans[idx]
+               .filters_idx[0] = vlan_cfg.entry_index;
+
+   /* mark as unused */
+       priv->port_info[port]
+               .vlan.egress_vlan_block_info.vlans[idx]
+               .filters_idx[1] = IDX_INVAL;
+
+       priv->port_info[port].vlan.egress_vlan_block_info.vlans[idx].vid = vid;
+       priv->port_info[port].vlan.egress_vlan_block_info.vlans[idx].used = true;
+       priv->port_info[port].vlan.egress_vlan_block_info.vlans[idx].untagged = untagged;
+
+EXIT:
+       return ret;
+}
+
+
+static int
+__prepare_vlan_egress_filters_off_sp_tag_cpu(struct dsa_switch *ds, uint8_t cpu_port)
+{
+       int ret = -EINVAL;
+       struct mxl862xx_priv *priv = ds->priv;
+       mxl862xx_extendedvlan_config_t vlan_cfg = { 0 };
+       struct mxl862xx_extended_vlan_block_info *block_info =
+               &priv->port_info[cpu_port].vlan.egress_vlan_block_info;
+
+       /* Allocate new block if needed */
+       if (!(block_info->allocated)) {
+               mxl862xx_extendedvlan_alloc_t vlan_alloc = { 0 };
+               /* Reserve fixed number of entries per port and direction */
+               vlan_alloc.number_of_entries = block_info->filters_max;
+               ret = mxl862xx_extended_vlan_alloc(&mxl_dev, &vlan_alloc);
+
+               if (ret != MXL862XX_STATUS_OK) {
+                       dev_err(priv->dev,
+                               "%s: Extended VLAN allocation for port%d egress filtering failed with %d\n",
+                               __func__, cpu_port, ret);
+                       goto EXIT;
+               }
+
+               block_info->allocated = true;
+               block_info->block_id = vlan_alloc.extended_vlan_block_id;
+       }
+
+       // Entry last - 1  : Outer and Inner tags are present.
+       // Transparent mode, no tag modifications
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id = block_info->block_id;
+
+       vlan_cfg.entry_index = block_info->filters_max - 2;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+       vlan_cfg.treatment.remove_tag = MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG;
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(priv->dev,
+                       "%s: Port:%d failed to add entry:%d for egress extended VLAN block ID:%d\n",
+                       __func__, cpu_port, vlan_cfg.entry_index,
+                       vlan_cfg.extended_vlan_block_id);
+               goto EXIT;
+       }
+
+       block_info->final_filters_idx = vlan_cfg.entry_index;
+
+       // Entry last : Outer tag is present.
+       // Transparent mode, no tag modifications
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id = block_info->block_id;
+
+       vlan_cfg.entry_index = block_info->filters_max - 1;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+       vlan_cfg.treatment.remove_tag = MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG;
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(priv->dev,
+                       "%s: Port:%d failed to add entry:%d for egress extended VLAN block ID:%d\n",
+                       __func__, cpu_port, vlan_cfg.entry_index,
+                       vlan_cfg.extended_vlan_block_id);
+               goto EXIT;
+       }
+
+       block_info->final_filters_idx = vlan_cfg.entry_index;
+
+EXIT:
+       return ret;
+
+}
+
+
+static int
+__prepare_vlan_egress_filters_sp_tag_cpu(struct dsa_switch *ds, uint8_t cpu_port, uint16_t vid, bool untagged)
+{
+       int ret = -EINVAL;
+       struct mxl862xx_priv *priv = ds->priv;
+       int filter_0 = -1;
+       int filter_1 = -1;
+       uint16_t idx = 0;
+       mxl862xx_extendedvlan_config_t vlan_cfg = { 0 };
+
+       /* Allocate new block if needed */
+       if (!(priv->port_info[cpu_port].vlan.egress_vlan_block_info.allocated)) {
+               mxl862xx_extendedvlan_alloc_t vlan_alloc = { 0 };
+               /* Reserve fixed number of entries per port and direction */
+               vlan_alloc.number_of_entries =
+                       priv->port_info[cpu_port]
+                               .vlan.egress_vlan_block_info.filters_max;
+               ret = mxl862xx_extended_vlan_alloc(&mxl_dev, &vlan_alloc);
+
+               if (ret != MXL862XX_STATUS_OK) {
+                       dev_err(priv->dev,
+                               "%s: Extended VLAN allocation for port%d egress filtering failed with %d\n",
+                               __func__, cpu_port, ret);
+                       goto EXIT;
+               }
+
+               priv->port_info[cpu_port].vlan.egress_vlan_block_info.allocated =
+                       true;
+               priv->port_info[cpu_port].vlan.egress_vlan_block_info.block_id =
+                       vlan_alloc.extended_vlan_block_id;
+       }
+
+       /* Populate the  block of rules related to VID */
+
+       /* VID specific entries must be processed before the final entries,
+        * so putting them at the beginnig of the block */
+       ret = __get_vlan_vid_filters_idx(priv, cpu_port, false, vid, &filter_0, &filter_1, &idx);
+
+       if (ret != MXL862XX_STATUS_OK)
+               goto EXIT;
+
+       // Entry 0 : Outer and Inner tags are present. If user port is untagged
+       // remove inner tag if the outer tag is matching the user port
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id =
+               priv->port_info[cpu_port].vlan.egress_vlan_block_info.block_id;
+       /* if found recycled entry reuse it, otherwise create new one */
+       vlan_cfg.entry_index =
+               filter_0 >= 0 ?
+                       filter_0 :
+                       priv->port_info[cpu_port]
+                               .vlan.egress_vlan_block_info.vid_filters_idx++;
+
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.filter.inner_vlan.vid_enable = true;
+       vlan_cfg.filter.inner_vlan.vid_val = vid;
+
+       if (untagged) {
+               vlan_cfg.treatment.remove_tag =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_2_TAG;
+               vlan_cfg.treatment.add_outer_vlan = true;
+               vlan_cfg.treatment.outer_vlan.vid_mode = MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_VID;
+               vlan_cfg.treatment.outer_vlan.tpid = MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q;
+               vlan_cfg.treatment.outer_vlan.priority_mode = MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_PRORITY;
+               vlan_cfg.treatment.outer_vlan.dei = MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_DEI;
+       }       else {
+               vlan_cfg.treatment.remove_tag = MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG;
+       }
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(priv->dev,
+                       "%s: Port:%d failed to add entry:%d for egress extended VLAN block ID:%d\n",
+                       __func__, cpu_port, vlan_cfg.entry_index,
+                       vlan_cfg.extended_vlan_block_id);
+               goto EXIT;
+       }
+
+       /* store VLAN filtering rules ID's (for VLAN delete, if needed) */
+       priv->port_info[cpu_port]
+               .vlan.egress_vlan_block_info.vlans[idx]
+               .filters_idx[0] = vlan_cfg.entry_index;
+
+   /* mark as unused */
+       priv->port_info[cpu_port]
+               .vlan.egress_vlan_block_info.vlans[idx]
+               .filters_idx[1] = IDX_INVAL;
+
+       priv->port_info[cpu_port].vlan.egress_vlan_block_info.vlans[idx].vid = vid;
+       priv->port_info[cpu_port].vlan.egress_vlan_block_info.vlans[idx].untagged = untagged;
+       priv->port_info[cpu_port].vlan.egress_vlan_block_info.vlans[idx].used = true;
+
+EXIT:
+       return ret;
+
+}
+
+
+static int
+__prepare_vlan_ingress_filters_sp_tag(struct dsa_switch *ds, uint8_t port, uint16_t vid)
+{
+       int ret = -EINVAL;
+       uint16_t idx = 0;
+       /* negative values if not found */
+       int filter_0 = -1;
+       int filter_1 = -1;
+       struct mxl862xx_priv *priv = ds->priv;
+
+       mxl862xx_extendedvlan_config_t vlan_cfg = { 0 };
+
+       /* Allocate new block if needed */
+       if (!(priv->port_info[port].vlan.ingress_vlan_block_info.allocated)) {
+               mxl862xx_extendedvlan_alloc_t vlan_alloc = { 0 };
+               /* Reserve fixed number of entries per port and direction */
+               vlan_alloc.number_of_entries =
+                       priv->port_info[port]
+                               .vlan.ingress_vlan_block_info.filters_max;
+               ret = mxl862xx_extended_vlan_alloc(&mxl_dev, &vlan_alloc);
+
+               if (ret != MXL862XX_STATUS_OK)
+                       goto EXIT;
+
+               priv->port_info[port].vlan.ingress_vlan_block_info.allocated =
+                       true;
+               priv->port_info[port].vlan.ingress_vlan_block_info.block_id =
+                       vlan_alloc.extended_vlan_block_id;
+       }
+
+       /* First populate the fixed block of rules which should be executed finally after
+        * VID specific filtering. The final rules (not related to VID)
+        * are placed at the end of the block. */
+
+       //Entry 6 no other rule applies Outer tag default Inner tag  not present DISCARD
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id =
+               priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+       vlan_cfg.entry_index = priv->port_info[port].vlan.ingress_vlan_block_info.filters_max - 1;
+       vlan_cfg.filter.outer_vlan.type =
+               MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+       vlan_cfg.treatment.remove_tag =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM;
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK)
+               goto EXIT;
+
+       priv->port_info[port].vlan.ingress_vlan_block_info.final_filters_idx = vlan_cfg.entry_index;
+
+       //Entry 5 no other rule applies Outer tag default Inner tag  present DISCARD
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id =
+               priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+       vlan_cfg.entry_index = priv->port_info[port].vlan.ingress_vlan_block_info.filters_max - 2;
+       vlan_cfg.filter.outer_vlan.type =
+               MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.treatment.remove_tag =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM;
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK)
+               goto EXIT;
+
+       priv->port_info[port].vlan.ingress_vlan_block_info.final_filters_idx = vlan_cfg.entry_index;
+
+       // Entry 4  untagged pkts. If there's PVID accept and add PVID tag, otherwise reject
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id =
+               priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+       vlan_cfg.entry_index = priv->port_info[port].vlan.ingress_vlan_block_info.filters_max - 3;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+       if (!priv->port_info[port].vlan.pvid) {
+               vlan_cfg.treatment.remove_tag =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM;
+       } else {
+               vlan_cfg.treatment.add_outer_vlan = true;
+               vlan_cfg.treatment.outer_vlan.vid_mode =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL;
+               vlan_cfg.treatment.outer_vlan.tpid =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q;
+               vlan_cfg.treatment.outer_vlan.priority_mode =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+               vlan_cfg.treatment.outer_vlan.priority_val = 0;
+               vlan_cfg.treatment.outer_vlan.dei =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0;
+               vlan_cfg.treatment.outer_vlan.vid_val = dsa_8021q_rx_vid(ds, port);
+               vlan_cfg.treatment.add_inner_vlan = true;
+               vlan_cfg.treatment.inner_vlan.vid_mode =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL;
+               vlan_cfg.treatment.inner_vlan.vid_val =
+                       priv->port_info[port].vlan.pvid;
+               vlan_cfg.treatment.inner_vlan.tpid =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q;
+               vlan_cfg.treatment.inner_vlan.priority_mode =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+               vlan_cfg.treatment.inner_vlan.priority_val = 0;
+               vlan_cfg.treatment.inner_vlan.dei =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0;
+       }
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(priv->dev,
+                       "%s: Port:%d failed to add PVID entry:%d for ingress extended VLAN block ID:%d\n",
+                       __func__, port, vlan_cfg.entry_index,
+                       vlan_cfg.extended_vlan_block_id);
+               goto EXIT;
+       }
+
+       priv->port_info[port].vlan.ingress_vlan_block_info.final_filters_idx =
+               vlan_cfg.entry_index;
+
+       // Entry 3 : Only Outer tag present : not matching  DISCARD
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id =
+               priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+       vlan_cfg.entry_index = priv->port_info[port].vlan.ingress_vlan_block_info.filters_max - 4;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+       vlan_cfg.treatment.remove_tag =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM;
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(priv->dev,
+                       "%s: Port:%d failed to add DISCARD entry:%d for ingress extended VLAN block ID:%d\n",
+                       __func__, port, vlan_cfg.entry_index,
+                       vlan_cfg.extended_vlan_block_id);
+               goto EXIT;
+       }
+
+       priv->port_info[port].vlan.ingress_vlan_block_info.final_filters_idx =
+               vlan_cfg.entry_index;
+
+       // Entry 2 : Outer and Inner VLAN tag present : not matching  DISCARD
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id =
+               priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+       vlan_cfg.entry_index = priv->port_info[port].vlan.ingress_vlan_block_info.filters_max - 5;
+
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.treatment.remove_tag =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM;
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(priv->dev,
+                       "%s: Port:%d failed to add DISCARD entry:%d for ingress extended VLAN block ID:%d\n",
+                       __func__, port, vlan_cfg.entry_index,
+                       vlan_cfg.extended_vlan_block_id);
+               goto EXIT;
+       }
+
+       priv->port_info[port].vlan.ingress_vlan_block_info.final_filters_idx = vlan_cfg.entry_index;
+
+       /* VID specific filtering rules which should be executed first before final ones.
+        * Storing starts at the beginning of the block. */
+
+       ret = __get_vlan_vid_filters_idx(priv, port, true, vid, &filter_0, &filter_1, &idx);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(priv->dev,
+                       "%s: Port:%d couldn't get idx for VID specific filters for VID:%d and  block ID:%d\n",
+                       __func__, port, vid, vlan_cfg.extended_vlan_block_id);
+               goto EXIT;
+       }
+
+       // Entry 0 : Outer and Inner VLAN tag present :  matching  ACCEPT
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id =
+               priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+       /* if found recycled entry reuse it, otherwise create new one */
+       vlan_cfg.entry_index =
+               filter_0 >= 0 ?
+                       filter_0 :
+                       priv->port_info[port]
+                               .vlan.ingress_vlan_block_info.vid_filters_idx++;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.filter.outer_vlan.vid_enable = true;
+       vlan_cfg.filter.outer_vlan.vid_val = vid;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.treatment.add_outer_vlan = true;
+       vlan_cfg.treatment.outer_vlan.vid_mode = MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL;
+       vlan_cfg.treatment.outer_vlan.vid_val = dsa_8021q_rx_vid(ds, port);
+       vlan_cfg.treatment.outer_vlan.tpid = MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q;
+       vlan_cfg.treatment.outer_vlan.priority_mode = MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+       vlan_cfg.treatment.outer_vlan.priority_val = 0;
+       vlan_cfg.treatment.outer_vlan.dei = MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0;
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(priv->dev,
+                       "%s: Port:%d failed to add entry:%d for block_id:%d\n",
+                       __func__, port, vlan_cfg.entry_index,
+                       vlan_cfg.extended_vlan_block_id);
+               goto EXIT;
+       }
+
+       /* store VLAN filtering rules ID's (for VLAN delete, if needed) */
+       priv->port_info[port]
+               .vlan.ingress_vlan_block_info.vlans[idx]
+               .filters_idx[0] = vlan_cfg.entry_index;
+
+       // Entry 1 : Only Outer tags is present : matching  ACCEPT
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id =
+               priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+       /* if found recycled entry reuse it, otherwise create new one */
+       vlan_cfg.entry_index =
+               filter_1 >= 0 ?
+                       filter_1 :
+                       priv->port_info[port]
+                               .vlan.ingress_vlan_block_info.vid_filters_idx++;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.filter.outer_vlan.vid_enable = true;
+       vlan_cfg.filter.outer_vlan.vid_val = vid;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+       vlan_cfg.treatment.add_outer_vlan = true;
+       vlan_cfg.treatment.outer_vlan.vid_mode = MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL;
+       vlan_cfg.treatment.outer_vlan.vid_val = dsa_8021q_rx_vid(ds, port);
+       vlan_cfg.treatment.outer_vlan.tpid = MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q;
+       vlan_cfg.treatment.outer_vlan.priority_mode = MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+       vlan_cfg.treatment.outer_vlan.priority_val = 0;
+       vlan_cfg.treatment.outer_vlan.dei = MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0;
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(priv->dev,
+                       "%s: Port:%d failed to add entry:%d for block_id:%d\n",
+                       __func__, port, vlan_cfg.entry_index,
+                       vlan_cfg.extended_vlan_block_id);
+               goto EXIT;
+       }
+
+       /* store VLAN filtering rules ID's (for VLAN delete, if needed) */
+       priv->port_info[port]
+               .vlan.ingress_vlan_block_info.vlans[idx]
+               .filters_idx[1] = vlan_cfg.entry_index;
+
+       priv->port_info[port].vlan.ingress_vlan_block_info.vlans[idx].vid = vid;
+       priv->port_info[port].vlan.ingress_vlan_block_info.vlans[idx].used = true;
+
+EXIT:
+       return ret;
+}
+
+
+static int
+__prepare_vlan_ingress_filters_sp_tag_cpu(struct dsa_switch *ds, uint8_t port, uint8_t cpu_port)
+{
+       int ret = -EINVAL;
+       uint16_t idx = 0;
+       /* negative values if not found */
+       int filter_0 = -1;
+       int filter_1 = -1;
+       struct mxl862xx_priv *priv = ds->priv;
+       uint16_t bridge_port_cpu = priv->port_info[port].bridge_port_cpu;
+       uint16_t vid = dsa_8021q_tx_vid(ds, port);
+
+       mxl862xx_extendedvlan_config_t vlan_cfg = { 0 };
+
+       /* Allocate new block if needed */
+       if (!(priv->port_info[cpu_port].vlan.ingress_vlan_block_info.allocated)) {
+               mxl862xx_extendedvlan_alloc_t vlan_alloc = { 0 };
+               /* Reserve fixed number of entries per port and direction */
+               vlan_alloc.number_of_entries =
+                       priv->port_info[cpu_port]
+                               .vlan.ingress_vlan_block_info.filters_max;
+               ret = mxl862xx_extended_vlan_alloc(&mxl_dev, &vlan_alloc);
+               if (ret != MXL862XX_STATUS_OK) {
+                       dev_err(priv->dev,
+                               "%s: Extended VLAN allocation for cpu_port%d ingress filtering failed with %d\n",
+                               __func__, cpu_port, ret);
+                       goto EXIT;
+               }
+
+               priv->port_info[cpu_port].vlan.ingress_vlan_block_info.allocated =
+                       true;
+               priv->port_info[cpu_port].vlan.ingress_vlan_block_info.block_id =
+                       vlan_alloc.extended_vlan_block_id;
+       }
+
+       /* VID specific filtering rules which should be executed first before final ones.
+        * Storing starts at the beginning of the block. */
+       ret = __get_vlan_vid_filters_idx(priv, cpu_port, true, vid, &filter_0, &filter_1, &idx);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(priv->dev,
+                       "%s: Port:%d couldn't get idx for VID specific filters for VID:%d and  block ID:%d\n",
+                       __func__, cpu_port, vid, vlan_cfg.extended_vlan_block_id);
+               goto EXIT;
+       }
+
+       // Entry 0 : Outer and Inner VLAN tag present
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id =
+               priv->port_info[cpu_port].vlan.ingress_vlan_block_info.block_id;
+       /* if found recycled entry reuse it, otherwise create new one */
+       vlan_cfg.entry_index =
+               filter_0 >= 0 ?
+                       filter_0 :
+                       priv->port_info[cpu_port]
+                               .vlan.ingress_vlan_block_info.vid_filters_idx++;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.filter.outer_vlan.vid_enable = true;
+       vlan_cfg.filter.outer_vlan.vid_val = vid;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.treatment.reassign_bridge_port = true;
+       vlan_cfg.treatment.new_bridge_port_id = bridge_port_cpu;
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(priv->dev,
+                       "%s: cpu_port:%d failed to add entry:%d for ingress extended VLAN block ID:%d\n",
+                       __func__, cpu_port, vlan_cfg.entry_index,
+                       vlan_cfg.extended_vlan_block_id);
+               goto EXIT;
+       }
+
+       /* store VLAN filtering rules ID's (for VLAN delete, if needed) */
+       priv->port_info[cpu_port]
+               .vlan.ingress_vlan_block_info.vlans[idx]
+               .filters_idx[0] = vlan_cfg.entry_index;
+
+       // Entry 1 : Only Outer tags is present
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id =
+               priv->port_info[cpu_port].vlan.ingress_vlan_block_info.block_id;
+       /* if found recycled entry reuse it, otherwise create new one */
+       vlan_cfg.entry_index =
+               filter_1 >= 0 ?
+                       filter_1 :
+                       priv->port_info[cpu_port]
+                               .vlan.ingress_vlan_block_info.vid_filters_idx++;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.filter.outer_vlan.vid_enable = true;
+       vlan_cfg.filter.outer_vlan.vid_val = vid;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+       vlan_cfg.treatment.reassign_bridge_port = true;
+       vlan_cfg.treatment.new_bridge_port_id = bridge_port_cpu;
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(priv->dev,
+                       "%s: cpu_port:%d failed to add entry:%d for ingress extended VLAN block ID:%d\n",
+                       __func__, cpu_port, vlan_cfg.entry_index,
+                       vlan_cfg.extended_vlan_block_id);
+               goto EXIT;
+       }
+
+       /* store VLAN filtering rules ID's (for VLAN delete, if needed) */
+       priv->port_info[cpu_port]
+               .vlan.ingress_vlan_block_info.vlans[idx]
+               .filters_idx[1] = vlan_cfg.entry_index;
+
+       priv->port_info[cpu_port].vlan.ingress_vlan_block_info.vlans[idx].vid = vid;
+       priv->port_info[cpu_port].vlan.ingress_vlan_block_info.vlans[idx].used = true;
+
+EXIT:
+       return ret;
+}
+
+static int
+__prepare_vlan_ingress_filters(struct dsa_switch *ds, uint8_t port, uint16_t vid)
+{
+       int ret = -EINVAL;
+       uint16_t idx = 0;
+       /* negative values if not found */
+       int filter_0 = -1;
+       int filter_1 = -1;
+       struct mxl862xx_priv *priv = ds->priv;
+
+       mxl862xx_extendedvlan_config_t vlan_cfg = { 0 };
+       /* Allocate new block if needed */
+       if (!(priv->port_info[port].vlan.ingress_vlan_block_info.allocated)) {
+               mxl862xx_extendedvlan_alloc_t vlan_alloc = { 0 };
+               /* Reserve fixed number of entries per port and direction */
+               vlan_alloc.number_of_entries =
+                       priv->port_info[port]
+                               .vlan.ingress_vlan_block_info.filters_max;
+               ret = mxl862xx_extended_vlan_alloc(&mxl_dev, &vlan_alloc);
+               if (ret != MXL862XX_STATUS_OK) {
+                       dev_err(priv->dev,
+                               "%s: Extended VLAN allocation for port%d ingress filtering failed with %d\n",
+                               __func__, port, ret);
+                       goto EXIT;
+               }
+
+               priv->port_info[port].vlan.ingress_vlan_block_info.allocated =
+                       true;
+               priv->port_info[port].vlan.ingress_vlan_block_info.block_id =
+                       vlan_alloc.extended_vlan_block_id;
+       }
+
+       /* First populate the block with set of rules which should be executed finally after
+        * VID specific filtering. The final rules (not related to VID) are placed on the end of the block. The number of
+        * rules is fixed  per port. Order of execution  is important. To avoid static reservations they are
+        * stored in reversed order starting from the end of the block */
+
+       //Entry 6 no other rule applies Outer tag default Inner tag  not present DISCARD
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id =
+               priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+       vlan_cfg.entry_index =
+               priv->port_info[port]
+                       .vlan.ingress_vlan_block_info.final_filters_idx--;
+       vlan_cfg.filter.outer_vlan.type =
+               MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+       vlan_cfg.treatment.remove_tag =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM;
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(priv->dev,
+                       "%s: Port:%d failed to add default DISCARD entry:%d for ingress extended VLAN block ID:%d\n",
+                       __func__, port, vlan_cfg.entry_index,
+                       vlan_cfg.extended_vlan_block_id);
+               goto EXIT;
+       }
+
+       //Entry 5 no other rule applies Outer tag default Inner tag  present DISCARD
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id =
+               priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+       vlan_cfg.entry_index =
+               priv->port_info[port]
+                       .vlan.ingress_vlan_block_info.final_filters_idx--;
+       vlan_cfg.filter.outer_vlan.type =
+               MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.treatment.remove_tag =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM;
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(priv->dev,
+                       "%s: Port:%d failed to add default DISCARD entry:%d for ingress extended VLAN block ID:%d\n",
+                       __func__, port, vlan_cfg.entry_index,
+                       vlan_cfg.extended_vlan_block_id);
+               goto EXIT;
+       }
+
+       // Entry 4  untagged pkts. If there's PVID accept and add PVID tag, otherwise reject
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id =
+               priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+       vlan_cfg.entry_index =
+               priv->port_info[port]
+                       .vlan.ingress_vlan_block_info.final_filters_idx--;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+       if (!priv->port_info[port].vlan.pvid) {
+               vlan_cfg.treatment.remove_tag =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM;
+       } else {
+               vlan_cfg.treatment.add_outer_vlan = true;
+               vlan_cfg.treatment.outer_vlan.vid_mode =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL;
+               vlan_cfg.treatment.outer_vlan.tpid =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q;
+               vlan_cfg.treatment.outer_vlan.priority_mode =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+               vlan_cfg.treatment.outer_vlan.priority_val = 0;
+               vlan_cfg.treatment.outer_vlan.dei =
+                       MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0;
+               vlan_cfg.treatment.outer_vlan.vid_val =
+                       priv->port_info[port].vlan.pvid;
+       }
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK)
+               goto EXIT;
+
+       // Entry 3 : Only Outer tag present : not matching  DISCARD
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id =
+               priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+       vlan_cfg.entry_index =
+               priv->port_info[port]
+                       .vlan.ingress_vlan_block_info.final_filters_idx--;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+       vlan_cfg.treatment.remove_tag =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM;
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK)
+               goto EXIT;
+
+       // Entry 2 : Outer and Inner VLAN tag present : not matching  DISCARD
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id =
+               priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+       vlan_cfg.entry_index =
+               priv->port_info[port]
+                       .vlan.ingress_vlan_block_info.final_filters_idx--;
+
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.treatment.remove_tag =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM;
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK)
+               goto EXIT;
+
+       /* VID specific filtering rules which should be executed first before final ones.
+        * Storing starts at the beginning of the block. */
+
+       ret = __get_vlan_vid_filters_idx(priv, port, true, vid, &filter_0, &filter_1, &idx);
+       if (ret != MXL862XX_STATUS_OK)
+               goto EXIT;
+
+       // Entry 0 : Outer and Inner VLAN tag present :  matching  ACCEPT
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id =
+               priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+       /* if found recycled entry reuse it, otherwise create new one */
+       vlan_cfg.entry_index =
+               filter_0 >= 0 ?
+                       filter_0 :
+                       priv->port_info[port]
+                               .vlan.ingress_vlan_block_info.vid_filters_idx++;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.filter.outer_vlan.vid_enable = true;
+       vlan_cfg.filter.outer_vlan.vid_val = vid;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.treatment.remove_tag =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG;
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK)
+               goto EXIT;
+
+       /* store VLAN filtering rules ID's (for VLAN delete, if needed) */
+       priv->port_info[port]
+               .vlan.ingress_vlan_block_info.vlans[idx]
+               .filters_idx[0] = vlan_cfg.entry_index;
+
+       // Entry 1 : Only Outer tag is present : matching  ACCEPT
+       memset(&vlan_cfg, 0, sizeof(vlan_cfg));
+       vlan_cfg.extended_vlan_block_id =
+               priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+       /* if found recycled entry reuse it, otherwise create new one */
+       vlan_cfg.entry_index =
+               filter_1 >= 0 ?
+                       filter_1 :
+                       priv->port_info[port]
+                               .vlan.ingress_vlan_block_info.vid_filters_idx++;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.filter.outer_vlan.vid_enable = true;
+       vlan_cfg.filter.outer_vlan.vid_val = vid;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG;
+       vlan_cfg.treatment.remove_tag =
+               MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG;
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK)
+               goto EXIT;
+
+       /* store VLAN filtering rules ID's (for VLAN delete, if needed) */
+       priv->port_info[port]
+               .vlan.ingress_vlan_block_info.vlans[idx]
+               .filters_idx[1] = vlan_cfg.entry_index;
+
+       priv->port_info[port].vlan.ingress_vlan_block_info.vlans[idx].vid = vid;
+       priv->port_info[port].vlan.ingress_vlan_block_info.vlans[idx].used = true;
+
+EXIT:
+       return ret;
+}
+
+static int mxl862xx_port_vlan_add(struct dsa_switch *ds, int port,
+                                  const struct switchdev_obj_port_vlan *vlan,
+                                  struct netlink_ext_ack *extack)
+{
+       int ret = -EINVAL;
+       struct mxl862xx_priv *priv = ds->priv;
+       mxl862xx_bridge_port_config_t br_port_cfg = { 0 };
+       bool untagged = vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED;
+       bool pvid = vlan->flags & BRIDGE_VLAN_INFO_PVID;
+       uint8_t cpu_port = priv->hw_info->cpu_port;
+       bool vlan_sp_tag = (priv->port_info[cpu_port].tag_protocol == DSA_TAG_PROTO_MXL862_8021Q);
+       bool standalone_port = false;
+       uint16_t vid = vlan->vid;
+
+       if (port < 0 || port >= MAX_PORTS) {
+               dev_err(priv->dev, "invalid port: %d\n", port);
+               NL_SET_ERR_MSG_MOD(extack, "Port out of range");
+               goto EXIT;
+       }
+
+       if (!((struct dsa_port *)dsa_to_port(ds, port))) {
+               dev_err(ds->dev, "%s:  port:%d is out of DSA domain\n", __func__, port);
+               NL_SET_ERR_MSG_MOD(extack, "Port out of DSA domain");
+               goto EXIT;
+       }
+
+       /* standalone port */
+       if ((priv->port_info[port].bridge == NULL) && (!dsa_is_cpu_port(ds, port)))
+               standalone_port = true;
+
+       if (vid == 0)
+               goto EXIT;
+
+       /* If this is request to set pvid, just overwrite it as there may be
+        * only one pid per port */
+       if (pvid)
+               priv->port_info[port].vlan.pvid = vid;
+       /* If this is pvid disable request, check if there's already matching vid
+        * and only then disable it. If vid doesn't match active pvid, don't touch it */
+       else {
+               if (priv->port_info[port].vlan.pvid == vid)
+                       priv->port_info[port].vlan.pvid = 0;
+       }
+
+       /* Check if there's enough room for ingress and egress rules */
+       if ((priv->port_info[port].vlan.ingress_vlan_block_info.final_filters_idx -
+                       priv->port_info[port].vlan.ingress_vlan_block_info.vid_filters_idx) <
+                       (priv->port_info[port].vlan.ingress_vlan_block_info.entries_per_vlan)) {
+
+               dev_err(ds->dev,
+                       "%s: Port:%d vlan:%d. Number of avaliable ingress entries too low. Required:%d  ff_idx:%d vf_idx:%d .\n",
+                       __func__, port, vid,
+                       priv->port_info[port].vlan.ingress_vlan_block_info.entries_per_vlan,
+                       priv->port_info[port].vlan.ingress_vlan_block_info.final_filters_idx,
+                       priv->port_info[port].vlan.ingress_vlan_block_info.vid_filters_idx);
+
+               ret = -ENOSPC;
+               NL_SET_ERR_MSG_MOD(extack, "Reached max number of VLAN ingress filter entries per port");
+               goto EXIT;
+       }
+
+       if ((priv->port_info[port].vlan.egress_vlan_block_info.final_filters_idx -
+                       priv->port_info[port].vlan.egress_vlan_block_info.vid_filters_idx) <
+                       (priv->port_info[port].vlan.egress_vlan_block_info.entries_per_vlan)) {
+
+               dev_err(ds->dev,
+                       "%s: Port:%d vlan:%d. Number of avaliable egress entries too low. Required:%d  ff_idx:%d vf_idx:%d .\n",
+                       __func__, port, vid,
+                       priv->port_info[port].vlan.egress_vlan_block_info.entries_per_vlan,
+                       priv->port_info[port].vlan.egress_vlan_block_info.final_filters_idx,
+                       priv->port_info[port].vlan.egress_vlan_block_info.vid_filters_idx);
+
+               ret = -ENOSPC;
+               NL_SET_ERR_MSG_MOD(extack, "Reached max number of VLAN egress filter entries per port");
+               goto EXIT;
+       }
+
+       /* Although 4-byte vlan special tagging handling is similar to 8 byte MxL tagging,
+        * keep VLAN rules separate for better readibility */
+       if (vlan_sp_tag) {
+               if (!dsa_is_cpu_port(ds, port)) {
+               /* Special rules for CPU port based on user port id */
+                       ret = __prepare_vlan_ingress_filters_sp_tag_cpu(ds, port, cpu_port);
+                       if (ret != MXL862XX_STATUS_OK) {
+                               dev_err(ds->dev,
+                                       "%s: Port:%d failed to prepare ingress filters for VLAN:%d with vlan_filtering disabled\n",
+                                       __func__, port, vid);
+                               NL_SET_ERR_MSG_MOD(extack, "Failed to prepare ingress filters with vlan_filtering disabled");
+                               goto EXIT;
+                       }
+                       ret = __prepare_vlan_egress_filters_sp_tag_cpu(ds, cpu_port, vid, untagged);
+                       if (ret != MXL862XX_STATUS_OK) {
+                               dev_err(ds->dev,
+                                       "%s: Port:%d failed to prepare egress filters for VLAN:%d with vlan_filtering disabled\n",
+                                       __func__, cpu_port, vid);
+                               NL_SET_ERR_MSG_MOD(extack, "Failed to prepare egress filters with vlan_filtering disabled");
+                               goto EXIT;
+                       }
+                       /* vlan_filtering disabled */
+                       /* skiping this configuration for vlan_sp_tag/cpu port as it requires special rules defined above */
+                       if (!priv->port_info[port].vlan.filtering) {
+                               dev_info(ds->dev,
+                                       "%s: port:%d setting VLAN:%d with vlan_filtering disabled\n",
+                                       __func__, port, vid);
+                               ret = __prepare_vlan_ingress_filters_off_sp_tag(ds, port, vid);
+                               if (ret != MXL862XX_STATUS_OK) {
+                                       dev_err(ds->dev,
+                                               "%s: Port:%d failed to prepare ingress filters for VLAN:%d with vlan_filtering disabled\n",
+                                               __func__, port, vid);
+                                       NL_SET_ERR_MSG_MOD(extack, "Failed to prepare ingress filters with vlan_filtering disabled");
+                                       goto EXIT;
+                               }
+
+                               ret = __prepare_vlan_egress_filters_off_sp_tag(ds, port, vid, untagged);
+                               if (ret != MXL862XX_STATUS_OK) {
+                                       dev_err(ds->dev,
+                                               "%s: Port:%d failed to prepare egress filters for VLAN:%d with vlan_filtering disabled\n",
+                                               __func__, port, vid);
+                                       NL_SET_ERR_MSG_MOD(extack, "Failed to prepare egress filters with vlan_filtering disabled");
+                                       goto EXIT;
+                               }
+                       }
+                       /* vlan_filtering enabled */
+                       else {
+                               /* special rules for the CPU port are already defined,
+                                * so define only the rules for user ports */
+                               ret = __prepare_vlan_ingress_filters_sp_tag(ds, port, vid);
+                               if (ret != MXL862XX_STATUS_OK) {
+                                       dev_err(ds->dev,
+                                               "%s: Port:%d failed to prepare ingress filters for VLAN:%d\n",
+                                               __func__, port, vid);
+                                       NL_SET_ERR_MSG_MOD(extack, "Failed to prepare ingress filters for VLAN");
+                                       goto EXIT;
+                               }
+
+                               ret = __prepare_vlan_egress_filters_sp_tag(ds, port, vid, untagged);
+                               if (ret != MXL862XX_STATUS_OK) {
+                                       dev_err(ds->dev,
+                                               "%s: Port:%d failed to prepare egress filters for VLAN:%d\n",
+                                               __func__, port, vid);
+                                       NL_SET_ERR_MSG_MOD(extack, "Failed to prepare egress filters for VLAN");
+                                       goto EXIT;
+                               }
+                       }
+               } else {
+                       /* CPU port. This else block handles explicit request for adding
+                        * VLAN to CPU port. Only egress rule requires reconfiguration.*/
+                       ret = __prepare_vlan_egress_filters_sp_tag_cpu(ds, cpu_port, vid, untagged);
+                       if (ret != MXL862XX_STATUS_OK) {
+                               dev_err(ds->dev,
+                                       "%s: Port:%d failed to prepare egress filters for VLAN:%d with vlan_filtering disabled\n",
+                                       __func__, cpu_port, vid);
+                               NL_SET_ERR_MSG_MOD(extack, "Failed to prepare egress filters with vlan_filtering disabled");
+                               goto EXIT;
+                       }
+               }
+
+               /* CPU port is explicitely added by the DSA framework to the new vlans.
+                  Apply block_id with filtering rules defined while processing user ports
+                       For 8021q special tag mode cpu port rules may change because of new ports added,
+                       so they need to be reloaded */
+               {
+                       mxl862xx_ctp_port_config_t ctp_param = { 0 };
+
+                       ctp_param.logical_port_id = cpu_port + 1;
+                       ctp_param.mask = MXL862XX_CTP_PORT_CONFIG_MASK_EGRESS_VLAN |
+                                            MXL862XX_CTP_PORT_CONFIG_MASK_INGRESS_VLAN;
+                       ctp_param.egress_extended_vlan_enable = true;
+                       ctp_param.egress_extended_vlan_block_id =
+                       priv->port_info[cpu_port].vlan.egress_vlan_block_info.block_id;
+                       ctp_param.ingress_extended_vlan_enable = true;
+                       ctp_param.ingress_extended_vlan_block_id =
+                               priv->port_info[cpu_port].vlan.ingress_vlan_block_info.block_id;
+
+                       ret = mxl862xx_ctp_port_config_set(&mxl_dev, &ctp_param);
+                       if (ret != MXL862XX_STATUS_OK) {
+                               dev_err(ds->dev,
+                                       "%s: CTP port %d config failed on port config set with %d\n",
+                                       __func__, cpu_port, ret);
+                               NL_SET_ERR_MSG_MOD(extack, "Failed to configure VLAN for cpu port");
+                               goto EXIT;
+                       }
+               }
+       }
+
+       /* VLAN rules for 8 byte MxL tagging*/
+       else {
+               /* vlan_filtering disabled */
+               /* skiping this configuration for vlan_sp_tag/cpu port as it requires special rules defined above */
+               if (!priv->port_info[port].vlan.filtering) {
+                       ret = __prepare_vlan_ingress_filters_off(priv, port, vid);
+                       if (ret != MXL862XX_STATUS_OK) {
+                               dev_err(ds->dev,
+                                       "%s: Port:%d failed to prepare ingress filters for VLAN:%d with vlan_filtering disabled\n",
+                                       __func__, port, vid);
+                               NL_SET_ERR_MSG_MOD(extack, "Failed to prepare ingress filters with vlan_filtering disabled");
+                               goto EXIT;
+                       }
+
+                       ret = __prepare_vlan_egress_filters_off(priv, port, vid, untagged);
+                       if (ret != MXL862XX_STATUS_OK) {
+                               dev_err(ds->dev,
+                                       "%s: Port:%d failed to prepare egress filters for VLAN:%d with vlan_filtering disabled\n",
+                                       __func__, port, vid);
+                               NL_SET_ERR_MSG_MOD(extack, "Failed to prepare egress filters with vlan_filtering disabled");
+                               goto EXIT;
+                       }
+               }
+               /* vlan_filtering enabled */
+               else {
+                       ret = __prepare_vlan_ingress_filters(ds, port, vid);
+                       if (ret != MXL862XX_STATUS_OK) {
+                               dev_err(ds->dev,
+                                       "%s: Port:%d failed to prepare ingress filters for VLAN:%d\n",
+                                       __func__, port, vid);
+                               NL_SET_ERR_MSG_MOD(extack, "Failed to prepare ingress filters for VLAN");
+                               goto EXIT;
+                       }
+                       ret = __prepare_vlan_egress_filters(ds, port, vid, untagged);
+                       if (ret != MXL862XX_STATUS_OK) {
+                               dev_err(ds->dev,
+                                       "%s: Port:%d failed to prepare egress filters for VLAN:%d\n",
+                                       __func__, port, vid);
+                               NL_SET_ERR_MSG_MOD(extack, "Failed to prepare egress filters for VLAN");
+                               goto EXIT;
+                       }
+               }
+
+               /* CPU port is explicitely added by the DSA framework to new vlans */
+               if (dsa_is_cpu_port(ds, port)) {
+                       mxl862xx_ctp_port_config_t ctp_param = { 0 };
+
+                       ctp_param.logical_port_id = port + 1;
+                       ctp_param.mask = MXL862XX_CTP_PORT_CONFIG_MASK_EGRESS_VLAN |
+                                            MXL862XX_CTP_PORT_CONFIG_MASK_INGRESS_VLAN;
+                       ctp_param.egress_extended_vlan_enable = true;
+                       ctp_param.egress_extended_vlan_block_id =
+                               priv->port_info[port].vlan.egress_vlan_block_info.block_id;
+                       ctp_param.ingress_extended_vlan_enable = true;
+                       ctp_param.ingress_extended_vlan_block_id =
+                               priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+
+                       ret = mxl862xx_ctp_port_config_set(&mxl_dev, &ctp_param);
+                       if (ret != MXL862XX_STATUS_OK) {
+                               dev_err(ds->dev,
+                                       "%s: CTP port %d config failed on port config set with %d\n",
+                                       __func__, port, ret);
+                               NL_SET_ERR_MSG_MOD(extack, "Failed to configure VLAN for cpu port");
+                               goto EXIT;
+                       }
+
+                       goto EXIT;
+               }
+       }
+
+       /* Update bridge port */
+       br_port_cfg.bridge_port_id = port + 1;
+       br_port_cfg.mask |= MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN |
+                            MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN |
+                                 MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING;
+       br_port_cfg.egress_extended_vlan_enable = true;
+       br_port_cfg.egress_extended_vlan_block_id =
+               priv->port_info[port].vlan.egress_vlan_block_info.block_id;
+       br_port_cfg.ingress_extended_vlan_enable = true;
+       br_port_cfg.ingress_extended_vlan_block_id =
+               priv->port_info[port].vlan.ingress_vlan_block_info.block_id;
+
+       /* Disable MAC learning for standalone ports. */
+       br_port_cfg.src_mac_learning_disable =
+                               (standalone_port) ? true : false;
+
+       ret = mxl862xx_bridge_port_config_set(&mxl_dev, &br_port_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               dev_err(ds->dev,
+                       "%s: Bridge port configuration for port %d failed with %d\n",
+                       __func__, port, ret);
+               NL_SET_ERR_MSG_MOD(extack, "Bridge port configuration for VLAN failed");
+               goto EXIT;
+       }
+
+EXIT:
+       return ret;
+}
+
+static int __deactivate_vlan_filter_entry(u16 block_id, u16 entry_idx)
+{
+       int ret = -EINVAL;
+       mxl862xx_extendedvlan_config_t vlan_cfg = { 0 };
+
+       /* Set default reset values as it makes the rule transparent */
+       vlan_cfg.extended_vlan_block_id = block_id;
+       vlan_cfg.entry_index = entry_idx;
+       vlan_cfg.filter.outer_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.filter.outer_vlan.priority_enable = true;
+       vlan_cfg.filter.outer_vlan.priority_val = 0;
+       vlan_cfg.filter.outer_vlan.vid_enable = true;
+       vlan_cfg.filter.outer_vlan.vid_val = 0;
+       vlan_cfg.filter.inner_vlan.type = MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL;
+       vlan_cfg.filter.inner_vlan.priority_enable = true;
+       vlan_cfg.filter.inner_vlan.priority_val = 0;
+       vlan_cfg.filter.inner_vlan.vid_enable = true;
+       vlan_cfg.filter.inner_vlan.vid_val = 0;
+       vlan_cfg.treatment.add_outer_vlan = true;
+       vlan_cfg.treatment.outer_vlan.priority_mode = MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+       vlan_cfg.treatment.outer_vlan.priority_val = 0;
+       vlan_cfg.treatment.add_inner_vlan = true;
+       vlan_cfg.treatment.inner_vlan.priority_mode = MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL;
+       vlan_cfg.treatment.inner_vlan.priority_val = 0;
+
+       ret = mxl862xx_extended_vlan_set(&mxl_dev, &vlan_cfg);
+       if (ret != MXL862XX_STATUS_OK) {
+               pr_err("%s: failed to deactivate entry:%d for VLAN block ID:%d\n",
+                       __func__, vlan_cfg.entry_index, vlan_cfg.extended_vlan_block_id);
+               goto exit;
+       }
+
+exit:
+       return ret;
+}
+
+static int mxl862xx_port_vlan_del(struct dsa_switch *ds, int port,
+                                 const struct switchdev_obj_port_vlan *vlan)
+{
+       int ret = -EINVAL;
+       int dir;
+       struct mxl862xx_priv *priv = ds->priv;
+       uint16_t vid = vlan->vid;
+
+       for (dir = 0 ; dir < 2 ; dir++) {
+               struct mxl862xx_extended_vlan_block_info *block_info = (dir == 0)
+                       ? &priv->port_info[port].vlan.ingress_vlan_block_info
+                       : &priv->port_info[port].vlan.egress_vlan_block_info;
+               char *dir_txt = (dir == 0)      ? "ingress" : "egress";
+               int16_t entry_idx;
+               int vlan_idx, x;
+               u16 block_id = block_info->block_id;
+               /* Indicator of the last dynamic vid related entry being processed.
+                * Required for cleanup of static rules at the end of the block. */
+               bool last_vlan = false;
+               bool vlan_found = false;
+
+               /* check if vlan is present */
+               for (vlan_idx = 0; vlan_idx < MAX_VLANS; vlan_idx++) {
+                       if ((block_info->vlans[vlan_idx].vid == vid)
+                                       && block_info->vlans[vlan_idx].used)
+                               vlan_found = true;
+
+                       if (vlan_idx == MAX_VLANS - 1)
+                               last_vlan = true;
+
+                       if (vlan_found)
+                               break;
+               }
+
+               if (!vlan_found) {
+                       dev_err(ds->dev, "%s: Port:%d VLAN:%d not found (%s)\n", __func__, port, vid, dir_txt);
+                       goto static_rules_cleanup;
+               }
+
+               /* cleanup */
+               for (x = 0; x < VID_RULES ; x++) {
+                       entry_idx = block_info->vlans[vlan_idx].filters_idx[x];
+                       if (entry_idx != IDX_INVAL) {
+                               ret = __deactivate_vlan_filter_entry(block_id, entry_idx);
+                               if (ret != MXL862XX_STATUS_OK)
+                                       goto EXIT;
+                       }
+               }
+
+               /* cleanup of the vlan record in the port vlan inventory */
+               block_info->vlans[vlan_idx].vid = 0;
+               block_info->vlans[vlan_idx].used = false;
+
+               /* find the first free slot for storing recycled filter entries */
+               for (x = 0; x < MAX_VLANS; x++) {
+                       if (!(block_info->filter_entries_recycled[x].valid)) {
+                               block_info->filter_entries_recycled[x].filters_idx[0] = block_info->vlans[vlan_idx].filters_idx[0];
+                               block_info->filter_entries_recycled[x].filters_idx[1] = block_info->vlans[vlan_idx].filters_idx[1];
+                               block_info->filter_entries_recycled[x].valid = true;
+                               block_info->vlans[vlan_idx].filters_idx[0] = IDX_INVAL;
+                               block_info->vlans[vlan_idx].filters_idx[1] = IDX_INVAL;
+                               break;
+                       }
+
+                       if (x == MAX_VLANS - 1) {
+                               ret = -ENOSPC;
+                               dev_err(ds->dev,
+                                       "%s: Port:%d no free slots for recycled %s filter entries\n",
+                                       __func__, port, dir_txt);
+                               goto EXIT;
+                       }
+               }
+
+static_rules_cleanup:
+               /* If this is the last vlan entry or no entries left,
+                * remove static entries (placed at the end of the block) */
+               if (last_vlan) {
+                       for (entry_idx = block_info->final_filters_idx; entry_idx < block_info->filters_max ; entry_idx++) {
+                               ret = __deactivate_vlan_filter_entry(block_id, entry_idx);
+                               if (ret != MXL862XX_STATUS_OK)
+                                       goto EXIT;
+                       }
+                       /* Entries cleared, so point out to the end */
+                       block_info->final_filters_idx = entry_idx;
+               }
+       }
+
+/*  The block release is not needed as the blocks/entries are distributed
+ *  evenly for all ports so it's static assignment. */
+
+EXIT:
+       return ret;
+}
+
+static int mxl862xx_port_fdb_add(struct dsa_switch *ds, int port,
+                                const unsigned char *addr, u16 vid, struct dsa_db db)
+{
+       int ret = -EINVAL;
+       struct mxl862xx_priv *priv = ds->priv;
+       mxl862xx_mac_table_add_t mac_table_add = { 0 };
+       uint8_t i = 0;
+
+       memcpy(mac_table_add.mac, addr, ETH_ALEN);
+
+       mac_table_add.port_id = port + 1;
+       mac_table_add.tci = (vid & 0xFFF);
+       mac_table_add.static_entry = true;
+
+       /* For CPU port add entries corresponding to all FIDs */
+       for (i = 0; i < priv->hw_info->phy_ports; i++) {
+
+               if (!(dsa_is_cpu_port(ds, port)))
+                       i = port;
+               /* Bypass entry add for the isolated port as it may turn back
+                * the traffic originated on the host to the cpu port */
+               if (priv->port_info[i].isolated)
+                       continue;
+
+               mac_table_add.fid = priv->port_info[i].bridgeID;
+               ret = mxl862xx_mac_table_entry_add(&mxl_dev, &mac_table_add);
+               if (ret != MXL862XX_STATUS_OK) {
+                       dev_err(ds->dev, "%s: Port:%d failed to add MAC table entry for FID:%d\n",
+                               __func__, port, mac_table_add.fid);
+                       goto EXIT;
+               }
+
+       if (!(dsa_is_cpu_port(ds, port)))
+               break;
+       }
+
+EXIT:
+       return ret;
+}
+
+static int mxl862xx_port_fdb_del(struct dsa_switch *ds, int port,
+                                const unsigned char *addr, u16 vid, struct dsa_db db)
+{
+       int ret = -EINVAL;
+       struct mxl862xx_priv *priv = ds->priv;
+       mxl862xx_mac_table_remove_t mac_table_remove = { 0 };
+       uint8_t i = 0;
+
+       memcpy(mac_table_remove.mac, addr, ETH_ALEN);
+       mac_table_remove.tci = (vid & 0xFFF);
+
+       /* For CPU port remove entries corresponding to all FIDs */
+       for (i = 0; i < priv->hw_info->phy_ports; i++) {
+               if (!(dsa_is_cpu_port(ds, port)))
+                       i = port;
+               mac_table_remove.fid = priv->port_info[i].bridgeID;
+               ret = mxl862xx_mac_table_entry_remove(&mxl_dev, &mac_table_remove);
+               if (ret != MXL862XX_STATUS_OK) {
+                       dev_err(ds->dev, "%s: Port:%d failed to remove MAC table entry for FID:%d\n",
+                               __func__, port, mac_table_remove.fid);
+                       goto EXIT;
+               }
+
+               if (!(dsa_is_cpu_port(ds, port)))
+                       break;
+       }
+
+EXIT:
+       return ret;
+}
+
+static int mxl862xx_port_fdb_dump(struct dsa_switch *ds, int port,
+                                 dsa_fdb_dump_cb_t *cb, void *data)
+{
+       int ret = -EINVAL;
+       mxl862xx_mac_table_read_t mac_table_read = { 0 };
+
+       mac_table_read.initial = 1;
+
+       for (;;) {
+               ret = mxl862xx_mac_table_entry_read(&mxl_dev, &mac_table_read);
+               if (ret != MXL862XX_STATUS_OK) {
+                       dev_err(ds->dev,
+                               "%s: Port:%d failed to read MAC table entry\n",
+                               __func__, port);
+                       goto EXIT;
+               }
+
+               if (mac_table_read.last == 1)
+                       break;
+
+               if (mac_table_read.port_id == port + 1)
+                       cb(mac_table_read.mac, mac_table_read.tci & 0x0FFF,
+                          mac_table_read.static_entry, data);
+
+               memset(&mac_table_read, 0, sizeof(mac_table_read));
+       }
+
+EXIT:
+       return ret;
+}
+
+static int mxl862xx_port_pre_bridge_flags(struct dsa_switch *ds, int port,
+       struct switchdev_brport_flags flags, struct netlink_ext_ack *extack)
+{
+       int ret = 0;
+
+       if (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD | BR_LEARNING | BR_PORT_LOCKED)) {
+               dev_err(ds->dev, "%s: Port:%d unsupported bridge flags:0x%lx\n",
+                               __func__, port, flags.mask);
+               if (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD | BR_LEARNING | BR_PORT_LOCKED)) {
+                       ret = -EINVAL;
+                       NL_SET_ERR_MSG_MOD(extack, "Unsupported bridge flags:0x%lx");
+               }
+       }
+
+       return ret;
+}
+
+
+static int mxl862xx_port_bridge_flags(struct dsa_switch *ds, int port,
+       struct switchdev_brport_flags flags, struct netlink_ext_ack *extack)
+{
+       int ret = 0;
+       uint16_t bridge_id;
+       struct mxl862xx_priv *priv = ds->priv;
+       bool bridge_ctx = true;
+
+       if (!dsa_is_user_port(ds, port))
+               return 0;
+
+       /* .port_pre_bridge_flags is called after this function,
+        *  so the supported flags check is needed also here */
+       if (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD | BR_LEARNING | BR_PORT_LOCKED)) {
+               dev_err(ds->dev, "%s: Port:%d unsupported bridge flags:0x%lx\n",
+                               __func__, port, flags.mask);
+               if (flags.mask & ~(BR_FLOOD | BR_MCAST_FLOOD | BR_BCAST_FLOOD | BR_LEARNING | BR_PORT_LOCKED)) {
+                       ret = -EINVAL;
+                       goto EXIT;
+               }
+       }
+
+       bridge_id = priv->port_info[port].bridgeID;
+       if ((bridge_id == 0) || (priv->port_info[port].bridge == NULL))
+               bridge_ctx = false;
+
+       /* Handle flooding flags (bridge context) */
+       if (bridge_ctx && (flags.mask & (BR_FLOOD|BR_MCAST_FLOOD|BR_BCAST_FLOOD))) {
+               mxl862xx_bridge_config_t bridge_config = { 0 };
+
+               bridge_config.mask = MXL862XX_BRIDGE_CONFIG_MASK_FORWARDING_MODE;
+               bridge_config.bridge_id = bridge_id;
+
+               if (flags.mask & BR_FLOOD)
+                       bridge_config.forward_unknown_unicast = (flags.val & BR_FLOOD) ?
+                               MXL862XX_BRIDGE_FORWARD_FLOOD : MXL862XX_BRIDGE_FORWARD_DISCARD;
+               if (flags.mask & BR_MCAST_FLOOD) {
+                       bridge_config.forward_unknown_multicast_ip = (flags.val & BR_MCAST_FLOOD) ?
+                               MXL862XX_BRIDGE_FORWARD_FLOOD : MXL862XX_BRIDGE_FORWARD_DISCARD;
+                       bridge_config.forward_unknown_multicast_non_ip = bridge_config.forward_unknown_multicast_ip;
+               }
+               if (flags.mask & BR_BCAST_FLOOD)
+                       bridge_config.forward_broadcast = (flags.val & BR_BCAST_FLOOD) ?
+                       MXL862XX_BRIDGE_FORWARD_FLOOD : MXL862XX_BRIDGE_FORWARD_DISCARD;
+
+               ret = mxl862xx_bridge_config_set(&mxl_dev, &bridge_config);
+               if (ret != MXL862XX_STATUS_OK) {
+                       dev_err(ds->dev, "%s: Port:%d bridge:%d configuration  failed\n",
+                               __func__, port, bridge_config.bridge_id);
+                       NL_SET_ERR_MSG_MOD(extack, "Configuration of bridge flooding flags failed");
+                       goto EXIT;
+               }
+       }
+       /* Handle learning flag (bridge port context) */
+       if (flags.mask & BR_LEARNING) {
+               mxl862xx_bridge_port_config_t br_port_cfg = { 0 };
+
+               br_port_cfg.mask =      MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING;
+               br_port_cfg.bridge_port_id = port + 1;
+               br_port_cfg.src_mac_learning_disable = (flags.val & BR_LEARNING) ? false : true;
+               ret = mxl862xx_bridge_port_config_set(&mxl_dev, &br_port_cfg);
+               if (ret != MXL862XX_STATUS_OK) {
+                       dev_err(ds->dev,
+                               "%s: MAC learning disable for port %d failed with ret=%d\n",
+                               __func__, port, ret);
+                       NL_SET_ERR_MSG_MOD(extack, "Configuration of bridge port learning flags failed");
+                       goto EXIT;
+               }
+       }
+
+EXIT:
+       return ret;
+}
+
+static int mxl862xx_change_tag_protocol(struct dsa_switch *ds,
+                                    enum dsa_tag_protocol proto)
+{
+       int ret = MXL862XX_STATUS_OK;
+
+       dev_info(ds->dev, "%s: DSA tag protocol change not supported\n",  __func__);
+       return ret;
+}
+
+static enum dsa_tag_protocol mxl862xx_get_tag_protocol(struct dsa_switch *ds,
+                                                      int port, enum dsa_tag_protocol m)
+{
+       enum dsa_tag_protocol tag_proto;
+
+       tag_proto = __dt_parse_tag_proto(ds, port);
+
+       return tag_proto;
+}
+
+static const struct dsa_switch_ops mxl862xx_switch_ops = {
+       .get_ethtool_stats = mxl862xx_get_ethtool_stats,
+       .get_strings = mxl862xx_get_strings,
+       .get_sset_count = mxl862xx_get_sset_count,
+       .change_tag_protocol    = mxl862xx_change_tag_protocol,
+       .get_tag_protocol = mxl862xx_get_tag_protocol,
+       .phy_read = mxl862xx_phy_read,
+       .phy_write = mxl862xx_phy_write,
+       .phylink_mac_config = mxl862xx_phylink_mac_config,
+       .phylink_mac_link_down = mxl862xx_phylink_mac_link_down,
+       .phylink_mac_link_up = mxl862xx_phylink_mac_link_up,
+       .phylink_get_caps = mxl862xx_phylink_get_caps,
+       .set_ageing_time = mxl862xx_set_ageing_time,
+       .port_bridge_join = mxl862xx_port_bridge_join,
+       .port_bridge_leave = mxl862xx_port_bridge_leave,
+       .port_disable = mxl862xx_port_disable,
+       .port_enable = mxl862xx_port_enable,
+       .port_fast_age = mxl862xx_port_fast_age,
+       .port_stp_state_set = mxl862xx_port_stp_state_set,
+       .port_mirror_add = mxl862xx_port_mirror_add,
+       .port_mirror_del = mxl862xx_port_mirror_del,
+       .port_vlan_filtering = mxl862xx_port_vlan_filtering,
+       .port_vlan_add = mxl862xx_port_vlan_add,
+       .port_vlan_del = mxl862xx_port_vlan_del,
+       .port_fdb_add = mxl862xx_port_fdb_add,
+       .port_fdb_del = mxl862xx_port_fdb_del,
+       .port_fdb_dump = mxl862xx_port_fdb_dump,
+       .port_pre_bridge_flags = mxl862xx_port_pre_bridge_flags,
+       .port_bridge_flags = mxl862xx_port_bridge_flags,
+       .setup = mxl862xx_setup,
+};
+
+static int mxl862xx_probe(struct mdio_device *mdiodev)
+{
+       struct device *dev = &mdiodev->dev;
+       struct mxl862xx_priv *priv;
+       struct dsa_switch *ds;
+       int ret;
+       struct sys_fw_image_version sys_img_ver = { 0 };
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv) {
+               dev_err(dev, "%s: Error allocating mxl862xx switch\n",
+                       __func__);
+               return -ENOMEM;
+       }
+
+       priv->dev = dev;
+       priv->bus = mdiodev->bus;
+       priv->sw_addr = mdiodev->addr;
+       priv->hw_info = of_device_get_match_data(dev);
+       if (!priv->hw_info)
+               return -EINVAL;
+
+       mxl_dev.bus = mdiodev->bus;
+       mxl_dev.sw_addr = mdiodev->addr;
+
+       mutex_init(&priv->pce_table_lock);
+
+       ret = sys_misc_fw_version(&mxl_dev, &sys_img_ver);
+       if (ret < 0)
+               dev_err(dev, "\t%40s:\t0x%x\n",
+                       "fapi_GSW_FW_Version failed with ret code", ret);
+       else {
+               dev_info(dev, "\t%40s:\t%x\n", "Iv Major",
+                        sys_img_ver.iv_major);
+               dev_info(dev, "\t%40s:\t%x\n", "Iv Minor",
+                        sys_img_ver.iv_minor);
+               dev_info(dev, "\t%40s:\t%u\n", "Revision",
+                        sys_img_ver.iv_revision);
+               dev_info(dev, "\t%40s:\t%u\n", "Build Num",
+                        sys_img_ver.iv_build_num);
+       }
+
+       ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL);
+       if (!ds) {
+               dev_err(dev, "%s: Error allocating DSA switch\n", __func__);
+               return -ENOMEM;
+       }
+
+       ds->dev = dev;
+       ds->num_ports = priv->hw_info->max_ports;
+       ds->priv = priv;
+       ds->ops = priv->hw_info->ops;
+       ds->assisted_learning_on_cpu_port = true;
+
+       dev_set_drvdata(dev, ds);
+
+       ret = dsa_register_switch(ds);
+       if (ret) {
+               if (ret != -EPROBE_DEFER)
+                       dev_err(dev, "%s: Error %d register DSA switch\n",
+                               __func__, ret);
+               return ret;
+       }
+
+       if (!dsa_is_cpu_port(ds, priv->hw_info->cpu_port)) {
+               dev_err(dev,
+                       "wrong CPU port defined, HW only supports port: %i",
+                       priv->hw_info->cpu_port);
+               ret = -EINVAL;
+               dsa_unregister_switch(ds);
+       }
+
+       return ret;
+}
+
+static void mxl862xx_remove(struct mdio_device *mdiodev)
+{
+       struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev);
+
+       dsa_unregister_switch(ds);
+
+}
+
+static const struct mxl862xx_hw_info mxl86282_data = {
+       .max_ports = 9,
+       .phy_ports = 8,
+       .cpu_port = 8,
+       .ops = &mxl862xx_switch_ops,
+};
+
+static const struct mxl862xx_hw_info mxl86252_data = {
+       .max_ports = 9,
+       .phy_ports = 5,
+       .cpu_port = 8,
+       .ops = &mxl862xx_switch_ops,
+};
+
+static const struct of_device_id mxl862xx_of_match[] = {
+       { .compatible = "mxl,86282", .data = &mxl86282_data },
+       { .compatible = "mxl,86252", .data = &mxl86252_data },
+       { /* sentinel */ },
+};
+
+MODULE_DEVICE_TABLE(of, mxl862xx_of_match);
+
+static struct mdio_driver mxl862xx_driver = {
+       .probe  = mxl862xx_probe,
+       .remove = mxl862xx_remove,
+       .mdiodrv.driver = {
+               .name = "mxl862xx",
+               .of_match_table = mxl862xx_of_match,
+       },
+};
+
+mdio_module_driver(mxl862xx_driver);
+
+MODULE_DESCRIPTION("Driver for MaxLinear MxL862xx switch family");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:mxl862xx");
diff --git a/target/linux/mediatek/files/net/dsa/tag_mxl862xx.c b/target/linux/mediatek/files/net/dsa/tag_mxl862xx.c
new file mode 100644 (file)
index 0000000..470c708
--- /dev/null
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * net/dsa/tag_mxl862xx.c - DSA driver Special Tag support for MaxLinear 862xx switch chips
+ *
+ * Copyright (C) 2024 MaxLinear Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#include <linux/bitops.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <net/dsa.h>
+
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#else
+#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))
+#endif
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 2, 0))
+#include "dsa_priv.h"
+#else
+#include "tag.h"
+#endif
+
+#define MXL862_NAME    "mxl862xx"
+
+/* To define the outgoing port and to discover the incoming port a special
+ * tag is used by the GSW1xx.
+ *
+ *       Dest MAC       Src MAC    special TAG        EtherType
+ * ...| 1 2 3 4 5 6 | 1 2 3 4 5 6 | 1 2 3 4 5 6 7 8 | 1 2 |...
+ *                                |<--------------->|
+ */
+
+/* special tag in TX path header */
+#define MXL862_TX_HEADER_LEN 8
+
+#define MXL862_RX_HEADER_LEN 8
+
+/* Byte 7 */
+#define MXL862_IGP_EGP_SHIFT 0
+#define MXL862_IGP_EGP_MASK GENMASK(3, 0)
+
+static struct sk_buff *mxl862_tag_xmit(struct sk_buff *skb,
+                                      struct net_device *dev)
+{
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0))
+       int err;
+#endif
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0))
+       struct dsa_port *dp = dsa_slave_to_port(dev);
+#else
+       struct dsa_port *dp = dsa_user_to_port(dev);
+#endif
+       struct dsa_port *cpu_dp = dp->cpu_dp;
+       unsigned int cpu_port = cpu_dp->index + 1;
+       unsigned int usr_port = dp->index + 1;
+
+       u8 *mxl862_tag;
+
+       if (skb == NULL)
+               return skb;
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0))
+       err = skb_cow_head(skb, MXL862_TX_HEADER_LEN);
+       if (err)
+               return NULL;
+#endif
+
+       /* provide additional space 'MXL862_TX_HEADER_LEN' bytes */
+       skb_push(skb, MXL862_TX_HEADER_LEN);
+
+       /* shift MAC address to the beginnig of the enlarged buffer,
+        * releasing the space required for DSA tag (between MAC address and Ethertype) */
+       memmove(skb->data, skb->data + MXL862_TX_HEADER_LEN, 2 * ETH_ALEN);
+
+       /* special tag ingress */
+       mxl862_tag = skb->data + 2 * ETH_ALEN;
+       mxl862_tag[0] = 0x88;
+       mxl862_tag[1] = 0xc3;
+       mxl862_tag[2] = 0;
+       mxl862_tag[3] = 0;
+       mxl862_tag[4] = 0;
+       mxl862_tag[5] = usr_port + 16 - cpu_port;
+       mxl862_tag[6] = 0;
+       mxl862_tag[7] = (cpu_port)&MXL862_IGP_EGP_MASK;
+
+       return skb;
+}
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0))
+static struct sk_buff *mxl862_tag_rcv(struct sk_buff *skb,
+                                     struct net_device *dev,
+                                     struct packet_type *pt)
+#else
+static struct sk_buff *mxl862_tag_rcv(struct sk_buff *skb,
+                                     struct net_device *dev)
+#endif
+{
+       int port;
+       u8 *mxl862_tag;
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 2, 0))
+       struct dsa_port *dp;
+#endif
+
+       if (unlikely(!pskb_may_pull(skb, MXL862_RX_HEADER_LEN))) {
+               dev_warn_ratelimited(&dev->dev,
+                                    "Dropping packet, cannot pull SKB\n");
+               return NULL;
+       }
+
+       mxl862_tag = skb->data - 2;
+
+       if ((mxl862_tag[0] != 0x88) && (mxl862_tag[1] != 0xc3)) {
+               dev_warn_ratelimited(
+                       &dev->dev,
+                       "Dropping packet due to invalid special tag marker\n");
+               dev_warn_ratelimited(
+                       &dev->dev,
+                       "Rx Packet Tag: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",
+                       mxl862_tag[0], mxl862_tag[1], mxl862_tag[2],
+                       mxl862_tag[3], mxl862_tag[4], mxl862_tag[5],
+                       mxl862_tag[6], mxl862_tag[7]);
+               return NULL;
+       }
+
+       /* Get source port information */
+       port = (mxl862_tag[7] & MXL862_IGP_EGP_MASK) >> MXL862_IGP_EGP_SHIFT;
+       port = port - 1;
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0))
+       skb->dev = dsa_master_find_slave(dev, 0, port);
+#else
+       skb->dev = dsa_conduit_find_user(dev, 0, port);
+#endif
+       if (!skb->dev) {
+               dev_warn_ratelimited(
+                       &dev->dev,
+                       "Dropping packet due to invalid source port\n");
+               dev_warn_ratelimited(
+                       &dev->dev,
+                       "Rx Packet Tag: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n",
+                       mxl862_tag[0], mxl862_tag[1], mxl862_tag[2],
+                       mxl862_tag[3], mxl862_tag[4], mxl862_tag[5],
+                       mxl862_tag[6], mxl862_tag[7]);
+               return NULL;
+       }
+
+       /* remove the MxL862xx special tag between the MAC addresses and the current ethertype field. */
+       skb_pull_rcsum(skb, MXL862_RX_HEADER_LEN);
+       memmove(skb->data - ETH_HLEN,
+               skb->data - (ETH_HLEN + MXL862_RX_HEADER_LEN), 2 * ETH_ALEN);
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 2, 0))
+       dp = dsa_slave_to_port(skb->dev);
+       if (dp->bridge_dev)
+               skb->offload_fwd_mark = 1;
+#else
+       dsa_default_offload_fwd_mark(skb);
+#endif
+
+       return skb;
+}
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0))
+const struct dsa_device_ops mxl862_netdev_ops = {
+       .xmit = mxl862_tag_xmit,
+       .rcv = mxl862_tag_rcv,
+};
+#else
+
+static const struct dsa_device_ops mxl862_netdev_ops = {
+       .name = "mxl862",
+       .proto = DSA_TAG_PROTO_MXL862,
+       .xmit = mxl862_tag_xmit,
+       .rcv = mxl862_tag_rcv,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0))
+       .overhead = MXL862_RX_HEADER_LEN,
+#else
+       .needed_headroom = MXL862_RX_HEADER_LEN,
+#endif
+};
+
+MODULE_LICENSE("GPL");
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 2, 0))
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_MXL862);
+#else
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_MXL862, MXL862_NAME);
+#endif
+
+module_dsa_tag_driver(mxl862_netdev_ops);
+#endif
+
+MODULE_LICENSE("GPL");
diff --git a/target/linux/mediatek/files/net/dsa/tag_mxl862xx_8021q.c b/target/linux/mediatek/files/net/dsa/tag_mxl862xx_8021q.c
new file mode 100644 (file)
index 0000000..96e911d
--- /dev/null
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * net/dsa/tag_mxl862xx_8021q.c - DSA driver 802.1q based Special Tag support for MaxLinear 862xx switch chips
+ *
+ * Copyright (C) 2024 MaxLinear Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
+ *
+ */
+
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#else
+#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c))
+#endif
+
+#include <linux/dsa/8021q.h>
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(6, 1, 0))
+#include "tag_8021q.h"
+#endif
+#include <net/dsa.h>
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 2, 0))
+#include "dsa_priv.h"
+#else
+#include "tag.h"
+#endif
+
+
+#define MXL862_NAME    "mxl862xx"
+
+/* To define the outgoing port and to discover the incoming port
+ * a special 4-byte outer VLAN tag is used by the MxL862xx.
+ *
+ *       Dest MAC       Src MAC    special   optional  EtherType
+ *                                 outer     inner
+ *                                 VLAN tag  tag(s)
+ * ...| 1 2 3 4 5 6 | 1 2 3 4 5 6 | 1 2 3 4 | 1 2 3 4 | 1 2 |...
+ *                                |<------->|
+ */
+
+/* special tag in TX path header */
+
+/* The mxl862_8021q 4-byte tagging is not yet supported in
+ * kernels >= 5.16 due to differences in DSA 8021q tagging handlers.
+ * DSA tx/rx vid functions are not avaliable, so dummy
+ * functions are here to make the code compilable. */
+#if 0 
+ #(LINUX_VERSION_CODE >= KERNEL_VERSION (5, 16, 0))
+static u16 dsa_8021q_rx_vid(struct dsa_switch *ds, int port)
+{
+   return 0;
+}
+#endif
+
+static u16 dsa_8021q_tx_vid(struct dsa_switch *ds, int port)
+{
+       return 0;
+}
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION (5, 14, 0))
+static void dsa_8021q_rcv(struct sk_buff *skb, int *source_port, int *switch_id)
+{
+       u16 vid, tci;
+
+       skb_push_rcsum(skb, ETH_HLEN);
+       if (skb_vlan_tag_present(skb)) {
+               tci = skb_vlan_tag_get(skb);
+               __vlan_hwaccel_clear_tag(skb);
+       } else {
+               __skb_vlan_pop(skb, &tci);
+       }
+       skb_pull_rcsum(skb, ETH_HLEN);
+
+       vid = tci & VLAN_VID_MASK;
+
+       *source_port = dsa_8021q_rx_source_port(vid);
+       *switch_id = dsa_8021q_rx_switch_id(vid);
+       skb->priority = (tci & VLAN_PRIO_MASK) >> VLAN_PRIO_SHIFT;
+}
+
+/* If the ingress port offloads the bridge, we mark the frame as autonomously
+ * forwarded by hardware, so the software bridge doesn't forward in twice, back
+ * to us, because we already did. However, if we're in fallback mode and we do
+ * software bridging, we are not offloading it, therefore the dp->bridge_dev
+ * pointer is not populated, and flooding needs to be done by software (we are
+ * effectively operating in standalone ports mode).
+ */
+static inline void dsa_default_offload_fwd_mark(struct sk_buff *skb)
+{
+       struct dsa_port *dp = dsa_slave_to_port(skb->dev);
+
+       skb->offload_fwd_mark = !!(dp->bridge_dev);
+}
+#endif
+
+static struct sk_buff *mxl862_8021q_tag_xmit(struct sk_buff *skb,
+                                     struct net_device *dev)
+{
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0))
+       struct dsa_port *dp = dsa_slave_to_port(dev);
+#else
+       struct dsa_port *dp = dsa_user_to_port(dev);
+#endif
+       unsigned int port = dp->index ;
+
+       u16 tx_vid = dsa_8021q_tx_vid(dp->ds, port);
+       u16 queue_mapping = skb_get_queue_mapping(skb);
+       u8 pcp = netdev_txq_to_tc(dev, queue_mapping);
+
+
+       dsa_8021q_xmit(skb, dev, ETH_P_8021Q,
+                             ((pcp << VLAN_PRIO_SHIFT) | tx_vid));
+
+       return skb;
+}
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0))
+static struct sk_buff *mxl862_8021q_tag_rcv(struct sk_buff *skb,
+                                     struct net_device *dev,
+                                     struct packet_type *pt)
+#else
+static struct sk_buff *mxl862_8021q_tag_rcv(struct sk_buff *skb,
+                                     struct net_device *dev)
+#endif
+{
+       uint16_t vlan = ntohs(*(uint16_t*)(skb->data));
+       int port = dsa_8021q_rx_source_port(vlan);
+       int src_port = -1;
+       int switch_id = -1;
+
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0))
+       skb->dev = dsa_master_find_slave(dev, 0, port);
+#else
+       skb->dev = dsa_conduit_find_user(dev, 0, port);
+#endif
+       if (!skb->dev) {
+               dev_warn_ratelimited(&dev->dev,"Dropping packet due to invalid source port:%d\n", port);
+               return NULL;
+       }
+       /* removes Outer VLAN tag */
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 18, 0))
+       dsa_8021q_rcv(skb, &src_port, &switch_id);
+#else
+       dsa_8021q_rcv(skb, &src_port, &switch_id, NULL);
+#endif
+
+       dsa_default_offload_fwd_mark(skb);
+
+       return skb;
+}
+
+static const struct dsa_device_ops mxl862_8021q_netdev_ops = {
+       .name = "mxl862_8021q",
+       .proto = DSA_TAG_PROTO_MXL862_8021Q,
+       .xmit = mxl862_8021q_tag_xmit,
+       .rcv = mxl862_8021q_tag_rcv,
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0))
+       .overhead = VLAN_HLEN,
+#else
+       .needed_headroom        = VLAN_HLEN,
+#endif
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0) && \
+        LINUX_VERSION_CODE > KERNEL_VERSION(5, 10, 0))
+       .promisc_on_master      = true,
+#elif (LINUX_VERSION_CODE > KERNEL_VERSION(6, 7, 0))
+       .promisc_on_conduit = true,
+#endif
+};
+
+
+MODULE_LICENSE("GPL");
+#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 2, 0))
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_MXL862_8021Q);
+#else
+MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_MXL862_8021Q, MXL862_NAME);
+#endif
+
+module_dsa_tag_driver(mxl862_8021q_netdev_ops);
index e9f63938b2d34df548a333cb374f53e87beabb7a..8ef8c66a11e7543cd140a81177c779394fd5137c 100644 (file)
@@ -75,6 +75,9 @@ mediatek_setup_interfaces()
        comfast,cf-e393ax)
                ucidef_set_interfaces_lan_wan "lan1" eth1
                ;;
+       openwrt,two)
+               ucidef_set_interface_lan "mxl1 mxl2 mxl3 mxl4"
+               ;;
        cudy,ap3000outdoor-v1|\
        cudy,ap3000-v1|\
        cudy,re3000-v1|\
index fe85b9d3f14130bbad6609d5760cce39af7680fb..d7bf178332852fa10cb10ef72a1464533b4e62fd 100644 (file)
@@ -296,10 +296,13 @@ CONFIG_NEED_SG_DMA_LENGTH=y
 CONFIG_NET_DEVLINK=y
 CONFIG_NET_DSA=y
 CONFIG_NET_DSA_AN8855=y
+CONFIG_NET_DSA_MXL862=y
 CONFIG_NET_DSA_MT7530=y
 CONFIG_NET_DSA_MT7530_MDIO=y
 CONFIG_NET_DSA_MT7530_MMIO=y
 CONFIG_NET_DSA_TAG_MTK=y
+CONFIG_NET_DSA_TAG_MXL862=y
+CONFIG_NET_DSA_TAG_MXL862_8021Q=y
 CONFIG_NET_EGRESS=y
 CONFIG_NET_FLOW_LIMIT=y
 CONFIG_NET_INGRESS=y
index b188add1bd3e9e2c829d2459155d63b883a2da0f..187cbaef0436aded5db68364b8ade96f798ff0f2 100644 (file)
@@ -1324,6 +1324,21 @@ define Device/openwrt_one
 endef
 TARGET_DEVICES += openwrt_one
 
+define Device/openwrt_two
+  DEVICE_VENDOR := OpenWrt
+  DEVICE_MODEL := Two
+  DEVICE_DTS := mt7988a-openwrt-two
+  DEVICE_DTS_DIR := ../dts
+  DEVICE_DTC_FLAGS := --pad 4096
+  DEVICE_DTS_LOADADDR := 0x45f00000
+  KERNEL_LOADADDR := 0x46000000
+  KERNEL := kernel-bin | gzip
+  KERNEL_INITRAMFS := kernel-bin | lzma | \
+        fit lzma $$(KDIR)/image-$$(firstword $$(DEVICE_DTS)).dtb with-initrd | pad-to 64k
+  KERNEL_INITRAMFS_SUFFIX := .itb
+endef
+TARGET_DEVICES += openwrt_two
+
 define Device/qihoo_360t7
   DEVICE_VENDOR := Qihoo
   DEVICE_MODEL := 360T7
diff --git a/target/linux/mediatek/patches-6.6/990-50-smartrg-net-dsa-mxl862xx-add-driver.patch b/target/linux/mediatek/patches-6.6/990-50-smartrg-net-dsa-mxl862xx-add-driver.patch
new file mode 100644 (file)
index 0000000..59c80c9
--- /dev/null
@@ -0,0 +1,89 @@
+From: Chad Monroe <chad@monroe.io>
+Date: Fri, 11 Oct 2024 16:35:40 -0700
+Subject: [PATCH] net: dsa: mxl862xx: add driver for Maxlinear 862xx
+
+Add DSA driver for Maxlinear 86252 and 86282 switches
+
+Signed-off-by: Chad Monroe <chad@monroe.io>
+---
+ drivers/net/dsa/Kconfig  |    2 ++
+ drivers/net/dsa/Makefile |    1 +
+ include/net/dsa.h        |    4 ++++
+ net/dsa/Kconfig          |   13 +++++++++++++
+ net/dsa/Makefile         |    2 ++
+ 5 files changed, 22 insertions(+)
+
+--- a/drivers/net/dsa/Kconfig
++++ b/drivers/net/dsa/Kconfig
+@@ -83,6 +83,8 @@ source "drivers/net/dsa/ocelot/Kconfig"
+ source "drivers/net/dsa/qca/Kconfig"
++source "drivers/net/dsa/mxl862xx/Kconfig"
++
+ source "drivers/net/dsa/sja1105/Kconfig"
+ source "drivers/net/dsa/xrs700x/Kconfig"
+--- a/drivers/net/dsa/Makefile
++++ b/drivers/net/dsa/Makefile
+@@ -21,6 +21,7 @@ obj-y                                += b53/
+ obj-y                         += hirschmann/
+ obj-y                         += microchip/
+ obj-y                         += mv88e6xxx/
++obj-y                         += mxl862xx/
+ obj-y                         += ocelot/
+ obj-y                         += qca/
+ obj-y                         += realtek/
+--- a/include/net/dsa.h
++++ b/include/net/dsa.h
+@@ -56,6 +56,8 @@ struct phylink_link_state;
+ #define DSA_TAG_PROTO_RTL8_4T_VALUE           25
+ #define DSA_TAG_PROTO_RZN1_A5PSW_VALUE                26
+ #define DSA_TAG_PROTO_LAN937X_VALUE           27
++#define DSA_TAG_PROTO_MXL862_VALUE            28
++#define DSA_TAG_PROTO_MXL862_8021Q_VALUE      29
+ enum dsa_tag_protocol {
+       DSA_TAG_PROTO_NONE              = DSA_TAG_PROTO_NONE_VALUE,
+@@ -86,6 +88,8 @@ enum dsa_tag_protocol {
+       DSA_TAG_PROTO_RTL8_4T           = DSA_TAG_PROTO_RTL8_4T_VALUE,
+       DSA_TAG_PROTO_RZN1_A5PSW        = DSA_TAG_PROTO_RZN1_A5PSW_VALUE,
+       DSA_TAG_PROTO_LAN937X           = DSA_TAG_PROTO_LAN937X_VALUE,
++      DSA_TAG_PROTO_MXL862            = DSA_TAG_PROTO_MXL862_VALUE,
++      DSA_TAG_PROTO_MXL862_8021Q      = DSA_TAG_PROTO_MXL862_8021Q_VALUE,
+ };
+ struct dsa_switch;
+--- a/net/dsa/Kconfig
++++ b/net/dsa/Kconfig
+@@ -125,6 +125,19 @@ config NET_DSA_TAG_QCA
+         Say Y or M if you want to enable support for tagging frames for
+         the Qualcomm Atheros QCA8K switches.
++config NET_DSA_TAG_MXL862
++      tristate "Tag driver for MxL862xx switches"
++      help
++        Say Y or M if you want to enable support for tagging frames for the
++        Maxlinear MxL862xx switches.
++
++config NET_DSA_TAG_MXL862_8021Q
++      tristate "Tag driver for MxL862xx switches, based on VLAN tags"
++      help
++        Say Y or M if you want to enable support for tagging frames for the
++        Maxlinear MxL862xx switches. This tagging variant is based on 4-byte wide VLAN
++        tags
++
+ config NET_DSA_TAG_RTL4_A
+       tristate "Tag driver for Realtek 4 byte protocol A tags"
+       help
+--- a/net/dsa/Makefile
++++ b/net/dsa/Makefile
+@@ -28,6 +28,8 @@ obj-$(CONFIG_NET_DSA_TAG_HELLCREEK) += t
+ obj-$(CONFIG_NET_DSA_TAG_KSZ) += tag_ksz.o
+ obj-$(CONFIG_NET_DSA_TAG_LAN9303) += tag_lan9303.o
+ obj-$(CONFIG_NET_DSA_TAG_MTK) += tag_mtk.o
++obj-$(CONFIG_NET_DSA_TAG_MXL862) += tag_mxl862xx.o
++obj-$(CONFIG_NET_DSA_TAG_MXL862_8021Q) += tag_mxl862xx_8021q.o
+ obj-$(CONFIG_NET_DSA_TAG_NONE) += tag_none.o
+ obj-$(CONFIG_NET_DSA_TAG_OCELOT) += tag_ocelot.o
+ obj-$(CONFIG_NET_DSA_TAG_OCELOT_8021Q) += tag_ocelot_8021q.o
diff --git a/target/linux/mediatek/patches-6.6/990-51-smartrg-net-phy-mxl-gpy-add-mxl862xx-support.patch b/target/linux/mediatek/patches-6.6/990-51-smartrg-net-phy-mxl-gpy-add-mxl862xx-support.patch
new file mode 100644 (file)
index 0000000..8cfed15
--- /dev/null
@@ -0,0 +1,76 @@
+From: Chad Monroe <chad@monroe.io>
+Date: Fri, 11 Oct 2024 16:37:47 -0700
+Subject: [PATCH] net: phy: mxl-gpy: add mxl862xx support
+
+Add PHY driver support for Maxlinear 86252 and 86282 switches
+
+Signed-off-by: Chad Monroe <chad@monroe.io>
+---
+ drivers/net/phy/mxl-gpy.c |   28 ++++++++++++++++++++++++++++
+ 1 file changed, 28 insertions(+)
+
+--- a/drivers/net/phy/mxl-gpy.c
++++ b/drivers/net/phy/mxl-gpy.c
+@@ -19,6 +19,7 @@
+ /* PHY ID */
+ #define PHY_ID_GPYx15B_MASK   0xFFFFFFFC
+ #define PHY_ID_GPY21xB_MASK   0xFFFFFFF9
++#define PHY_ID_MXL862XX_MASK  0xFFFFFF00
+ #define PHY_ID_GPY2xx         0x67C9DC00
+ #define PHY_ID_GPY115B                0x67C9DF00
+ #define PHY_ID_GPY115C                0x67C9DF10
+@@ -31,6 +32,7 @@
+ #define PHY_ID_GPY241B                0x67C9DE40
+ #define PHY_ID_GPY241BM               0x67C9DE80
+ #define PHY_ID_GPY245B                0x67C9DEC0
++#define PHY_ID_MXL862XX               0xC1335500
+ #define PHY_CTL1              0x13
+ #define PHY_CTL1_MDICD                BIT(3)
+@@ -1097,6 +1099,15 @@ static int gpy_led_polarity_set(struct p
+       unreachable();
+ }
++static int gpy_c45_pma_read_abilities(struct phy_device *phydev)
++{
++      phydev->c45_ids.devices_in_package  |= MDIO_DEVS_AN;
++
++      genphy_c45_pma_read_abilities(phydev);
++
++      return 0;
++}
++
+ static struct phy_driver gpy_drivers[] = {
+       {
+               PHY_ID_MATCH_MODEL(PHY_ID_GPY2xx),
+@@ -1363,6 +1374,22 @@ static struct phy_driver gpy_drivers[] =
+               .set_loopback   = gpy_loopback,
+               .link_change_notify     = gpy_link_change_notify,
+       },
++      {
++              .phy_id         = PHY_ID_MXL862XX,
++              .phy_id_mask    = PHY_ID_MXL862XX_MASK,
++              .name           = "MaxLinear Ethernet MxL862XX",
++              .get_features   = gpy_c45_pma_read_abilities,
++              .config_init    = gpy_config_init,
++              .probe          = gpy_probe,
++              .suspend        = genphy_suspend,
++              .resume         = genphy_resume,
++              .config_aneg    = gpy_config_aneg,
++              .aneg_done      = genphy_c45_aneg_done,
++              .read_status    = gpy_read_status,
++              .config_intr    = gpy_config_intr,
++              .handle_interrupt = gpy_handle_interrupt,
++              .set_loopback   = gpy_loopback,
++      },
+ };
+ module_phy_driver(gpy_drivers);
+@@ -1379,6 +1406,7 @@ static struct mdio_device_id __maybe_unu
+       {PHY_ID_MATCH_MODEL(PHY_ID_GPY241B)},
+       {PHY_ID_MATCH_MODEL(PHY_ID_GPY241BM)},
+       {PHY_ID_MATCH_MODEL(PHY_ID_GPY245B)},
++      {PHY_ID_MXL862XX, PHY_ID_MXL862XX_MASK},
+       { }
+ };
+ MODULE_DEVICE_TABLE(mdio, gpy_tbl);