--- /dev/null
+// 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";
+};
--- /dev/null
+# 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.
--- /dev/null
+# 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
--- /dev/null
+// 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);
+}
+
--- /dev/null
+// 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_ */
--- /dev/null
+/* 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 */
--- /dev/null
+/* 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_ */
--- /dev/null
+// 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;
+}
--- /dev/null
+// 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_ */
--- /dev/null
+// 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);
--- /dev/null
+/* 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_ */
--- /dev/null
+/* 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_ */
--- /dev/null
+// 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_ */
--- /dev/null
+// 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_ */
--- /dev/null
+// 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, ¶m);
+ 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, ¶m);
+ 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, ¶m);
+ 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, ®ister_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, ®ister_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, ®ister_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, ®ister_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, ®ister_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, ¶m);
+ 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");
--- /dev/null
+// 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");
--- /dev/null
+// 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);
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|\
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
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
--- /dev/null
+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
--- /dev/null
+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);