From: John Crispin Date: Wed, 26 Feb 2025 17:18:05 +0000 (+0100) Subject: mxl862xx: add sdk driver X-Git-Url: http://git.lede-project.org./?a=commitdiff_plain;h=refs%2Fheads%2Fmxl862xx;p=openwrt%2Fstaging%2Fblogic.git mxl862xx: add sdk driver Signed-off-by: John Crispin --- diff --git a/target/linux/mediatek/dts/mt7988a-openwrt-two.dts b/target/linux/mediatek/dts/mt7988a-openwrt-two.dts new file mode 100644 index 000000000000..fe2e6eaf3fd2 --- /dev/null +++ b/target/linux/mediatek/dts/mt7988a-openwrt-two.dts @@ -0,0 +1,193 @@ +// SPDX-License-Identifier: (GPL-2.0 OR MIT) + +/dts-v1/; +#include "mt7988a-rfb.dts" +#include +#include +#include + +/ { + 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 = ; + }; + }; + + spic_pins: spi1-pins { + mux { + function = "spi"; + groups = "spi1"; + }; + }; +}; + +&mdio_bus { + #address-cells = <1>; + #size-cells = <0>; + reset-gpios = <&pio 72 GPIO_ACTIVE_LOW>; + reset-assert-us = <100000>; + reset-deassert-us = <100000>; + + phy0: ethernet-phy@0 { + reg = <0>; + compatible = "ethernet-phy-ieee802.3-c45"; + }; + + phy8: ethernet-phy@8 { + reg = <8>; + compatible = "ethernet-phy-ieee802.3-c45"; + }; + + switch@10 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "mxl,86252"; + reg = <0x10>; + dsa,member = <1 0>; + + status = "okay"; + + reset-gpios = <&pio 1 GPIO_ACTIVE_LOW>; + reset-assert-us = <100000>; + reset-deassert-us = <1000000>; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + label = "mxl1"; + phy-handle = <&swphy0>; + phy-mode = "internal"; + status = "okay"; + }; + + port@1 { + reg = <1>; + label = "mxl2"; + phy-handle = <&swphy1>; + phy-mode = "internal"; + status = "okay"; + }; + + port@2 { + reg = <2>; + label = "mxl3"; + phy-handle = <&swphy2>; + phy-mode = "internal"; + status = "okay"; + }; + + port@3 { + reg = <3>; + label = "mxl4"; + phy-handle = <&swphy3>; + phy-mode = "internal"; + status = "okay"; + }; + + port@8 { + reg = <8>; + label = "cpu"; + phy-mode = "usxgmii"; + ethernet = <&gmac2>; + dsa-tag-protocol = "mxl862"; + // dsa-tag-protocol = "mxl862_8021q"; + + fixed-link { + speed = <10000>; + full-duplex; + }; + }; + + /** + * not supported by MXL DSA driver yet + ** + port@9 { + reg = <9>; + label = "lan9"; + phy-handle = <&phy0>; + phy-mode = "usxgmii"; + status = "okay"; + }; + */ + }; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + + swphy0: swphy@0 { + reg = <0>; + }; + swphy1: swphy@1 { + reg = <1>; + }; + swphy2: swphy@2 { + reg = <2>; + }; + swphy3: swphy@3 { + reg = <3>; + }; + }; + }; +}; + +&gmac0 { +}; + +&gmac1 { + phy-mode = "10gbase-r"; + + status = "okay"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; +}; + +&gmac2 { + label = "dsa1"; + phy-mode = "10gbase-r"; + + status = "okay"; + + fixed-link { + speed = <10000>; + full-duplex; + pause; + }; +}; + +&uart1 { + status = "okay"; +}; + +&spi1 { + pinctrl-names = "default"; + /* pin shared with snfi */ + pinctrl-0 = <&spic_pins>; + status = "disabled"; +}; + +&pcie0 { + status = "okay"; +}; diff --git a/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/Kconfig b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/Kconfig new file mode 100644 index 000000000000..5b4e05181dc6 --- /dev/null +++ b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/Kconfig @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only +config NET_DSA_MXL862 + tristate "MaxLinear MxL862xx" + depends on NET_DSA + select NET_DSA_TAG_MXL862 + help + This enables support for the MaxLinear MxL862xx switch family. diff --git a/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/Makefile b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/Makefile new file mode 100644 index 000000000000..62473cd0d295 --- /dev/null +++ b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/Makefile @@ -0,0 +1,3 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_NET_DSA_MXL862) += mxl862xx_dsa.o +mxl862xx_dsa-y := mxl862xx.o host_api/mxl862xx_api.o host_api/mxl862xx_host_api_impl.o host_api/mxl862xx_mdio_relay.o diff --git a/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_api.c b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_api.c new file mode 100644 index 000000000000..92a74df0a049 --- /dev/null +++ b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_api.c @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * drivers/net/dsa/host_api/mxl862xx_api.c - dsa driver for Maxlinear mxl862xx switch chips family + * + * Copyright (C) 2024 MaxLinear Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "mxl862xx_host_api_impl.h" +#include "mxl862xx_api.h" +#include "mxl862xx_mmd_apis.h" + +int sys_misc_fw_version(const mxl862xx_device_t *dev, + struct sys_fw_image_version *sys_img_ver) +{ + return mxl862xx_api_wrap(dev, SYS_MISC_FW_VERSION, sys_img_ver, + sizeof(*sys_img_ver), 0, sizeof(*sys_img_ver)); +} + +int mxl862xx_register_mod(const mxl862xx_device_t *dev, mxl862xx_register_mod_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_COMMON_REGISTERMOD, parm, sizeof(*parm), 0, + 0); +} + +int mxl862xx_port_link_cfg_set(const mxl862xx_device_t *dev, mxl862xx_port_link_cfg_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_COMMON_PORTLINKCFGSET, parm, sizeof(*parm), + MXL862XX_COMMON_PORTLINKCFGGET, 0); +} + +int mxl862xx_port_cfg_get(const mxl862xx_device_t *dev, mxl862xx_port_cfg_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_COMMON_PORTCFGGET, parm, sizeof(*parm), 0, + sizeof(*parm)); +} + +int mxl862xx_port_cfg_set(const mxl862xx_device_t *dev, mxl862xx_port_cfg_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_COMMON_PORTCFGSET, parm, sizeof(*parm), + MXL862XX_COMMON_PORTCFGGET, 0); +} + +int mxl862xx_bridge_alloc(const mxl862xx_device_t *dev, mxl862xx_bridge_alloc_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_BRIDGE_ALLOC, parm, sizeof(*parm), 0, + sizeof(*parm)); +} +int mxl862xx_bridge_free(const mxl862xx_device_t *dev, mxl862xx_bridge_alloc_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_BRIDGE_FREE, parm, sizeof(*parm), 0, 0); +} + +int mxl862xx_bridge_config_set(const mxl862xx_device_t *dev, mxl862xx_bridge_config_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_BRIDGE_CONFIGSET, parm, sizeof(*parm), + MXL862XX_BRIDGE_CONFIGGET, 0); +} + +int mxl862xx_bridge_config_get(const mxl862xx_device_t *dev, mxl862xx_bridge_config_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_BRIDGE_CONFIGGET, parm, sizeof(*parm), 0, + sizeof(*parm)); +} + +int mxl862xx_bridge_port_alloc(const mxl862xx_device_t *dev, mxl862xx_bridge_port_alloc_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_BRIDGEPORT_ALLOC, parm, sizeof(*parm), 0, + sizeof(*parm)); +} + +int mxl862xx_bridge_port_free(const mxl862xx_device_t *dev, mxl862xx_bridge_port_alloc_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_BRIDGEPORT_FREE, parm, sizeof(*parm), 0, 0); +} + +int mxl862xx_bridge_port_config_set(const mxl862xx_device_t *dev, + mxl862xx_bridge_port_config_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_BRIDGEPORT_CONFIGSET, parm, sizeof(*parm), + MXL862XX_BRIDGEPORT_CONFIGGET, 0); +} + +int mxl862xx_bridge_port_config_get(const mxl862xx_device_t *dev, + mxl862xx_bridge_port_config_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_BRIDGEPORT_CONFIGGET, parm, sizeof(*parm), + 0, sizeof(*parm)); +} + +int mxl862xx_debug_rmon_port_get(const mxl862xx_device_t *dev, + mxl862xx_debug_rmon_port_cnt_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_DEBUG_RMON_PORT_GET, parm, sizeof(*parm), + 0, sizeof(*parm)); +} + +int mxl862xx_mac_table_clear(const mxl862xx_device_t *dev) +{ + return mxl862xx_api_wrap(dev, MXL862XX_MAC_TABLECLEAR, NULL, 0, 0, 0); +} + +int mxl862xx_mac_table_clear_cond(const mxl862xx_device_t *dev, + mxl862xx_mac_table_clear_cond_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_MAC_TABLECLEARCOND, parm, sizeof(*parm), 0, + 0); +} + +int mxl862xx_mac_table_entry_read(const mxl862xx_device_t *dev, + mxl862xx_mac_table_read_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_MAC_TABLEENTRYREAD, parm, sizeof(*parm), 0, + sizeof(*parm)); +} + +int mxl862xx_mac_table_entry_add(const mxl862xx_device_t *dev, + mxl862xx_mac_table_add_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_MAC_TABLEENTRYADD, parm, sizeof(*parm), 0, + 0); +} + +int mxl862xx_mac_table_entry_remove(const mxl862xx_device_t *dev, + mxl862xx_mac_table_remove_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_MAC_TABLEENTRYREMOVE, parm, sizeof(*parm), + 0, 0); +} + +int mxl862xx_stp_port_cfg_set(const mxl862xx_device_t *dev, mxl862xx_stp_port_cfg_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_STP_PORTCFGSET, parm, sizeof(*parm), + MXL862XX_STP_PORTCFGGET, 0); +} + +int mxl862xx_ss_sp_tag_get(const mxl862xx_device_t *dev, mxl862xx_ss_sp_tag_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_SS_SPTAG_GET, parm, sizeof(*parm), 0, + sizeof(*parm)); +} + +int mxl862xx_ss_sp_tag_set(const mxl862xx_device_t *dev, mxl862xx_ss_sp_tag_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_SS_SPTAG_SET, parm, sizeof(*parm), + MXL862XX_SS_SPTAG_GET, sizeof(*parm)); +} + +int mxl862xx_ctp_port_config_get(const mxl862xx_device_t *dev, + mxl862xx_ctp_port_config_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_CTP_PORTCONFIGGET, parm, sizeof(*parm), 0, + sizeof(*parm)); +} + +int mxl862xx_ctp_port_config_set(const mxl862xx_device_t *dev, + mxl862xx_ctp_port_config_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_CTP_PORTCONFIGSET, parm, sizeof(*parm), + MXL862XX_CTP_PORTCONFIGGET, 0); +} + +int mxl862xx_ctp_port_assignment_set(const mxl862xx_device_t *dev, + mxl862xx_ctp_port_assignment_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_CTP_PORTASSIGNMENTSET, parm, sizeof(*parm), + MXL862XX_CTP_PORTASSIGNMENTGET, 0); +} + +int mxl862xx_ctp_port_assignment_get(const mxl862xx_device_t *dev, + mxl862xx_ctp_port_assignment_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_CTP_PORTASSIGNMENTGET, parm, sizeof(*parm), + 0, sizeof(*parm)); +} + +int mxl862xx_monitor_port_cfg_get(const mxl862xx_device_t *dev, + mxl862xx_monitor_port_cfg_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_COMMON_MONITORPORTCFGGET, parm, + sizeof(*parm), 0, sizeof(*parm)); +} + +int mxl862xx_monitor_port_cfg_set(const mxl862xx_device_t *dev, + mxl862xx_monitor_port_cfg_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_COMMON_MONITORPORTCFGSET, parm, + sizeof(*parm), MXL862XX_COMMON_MONITORPORTCFGGET, 0); +} + +int mxl862xx_extended_vlan_alloc(const mxl862xx_device_t *dev, + mxl862xx_extendedvlan_alloc_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_EXTENDEDVLAN_ALLOC, parm, sizeof(*parm), 0, + sizeof(*parm)); +} + +int mxl862xx_extended_vlan_set(const mxl862xx_device_t *dev, + mxl862xx_extendedvlan_config_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_EXTENDEDVLAN_SET, parm, sizeof(*parm), + MXL862XX_EXTENDEDVLAN_GET, 0); +} + +int mxl862xx_extended_vlan_get(const mxl862xx_device_t *dev, + mxl862xx_extendedvlan_config_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_EXTENDEDVLAN_GET, parm, sizeof(*parm), 0, + sizeof(*parm)); +} + +int mxl862xx_extended_vlan_free(const mxl862xx_device_t *dev, + mxl862xx_extendedvlan_alloc_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_EXTENDEDVLAN_FREE, parm, sizeof(*parm), 0, + 0); +} + +int mxl862xx_vlan_filter_alloc(const mxl862xx_device_t *dev, + mxl862xx_vlanfilter_alloc_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_VLANFILTER_ALLOC, parm, sizeof(*parm), 0, + sizeof(*parm)); +} + +int mxl862xx_vlan_filter_set(const mxl862xx_device_t *dev, + mxl862xx_vlanfilter_config_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_VLANFILTER_SET, parm, sizeof(*parm), + MXL862XX_VLANFILTER_GET, 0); +} + +int mxl862xx_vlan_filter_get(const mxl862xx_device_t *dev, + mxl862xx_vlanfilter_config_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_VLANFILTER_GET, parm, sizeof(*parm), 0, + sizeof(*parm)); +} + +int mxl862xx_vlan_filter_free(const mxl862xx_device_t *dev, + mxl862xx_vlanfilter_alloc_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_VLANFILTER_FREE, parm, sizeof(*parm), 0, + 0); +} + +int mxl862xx_cfg_get(const mxl862xx_device_t *dev, mxl862xx_cfg_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_COMMON_CFGGET, parm, sizeof(*parm), 0, + sizeof(*parm)); +} + +int mxl862xx_cfg_set(const mxl862xx_device_t *dev, mxl862xx_cfg_t *parm) +{ + return mxl862xx_api_wrap(dev, MXL862XX_COMMON_CFGSET, parm, sizeof(*parm), + MXL862XX_COMMON_CFGGET, 0); +} + diff --git a/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_api.h b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_api.h new file mode 100644 index 000000000000..d222952a1d35 --- /dev/null +++ b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_api.h @@ -0,0 +1,1826 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * drivers/net/dsa/host_api/mxl862xx_api.h - DSA Driver for MaxLinear Mxl862xx switch chips family + * + * Copyright (C) 2024 MaxLinear Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef _MXL862XX_API_H_ +#define _MXL862XX_API_H_ + +#include "mxl862xx_types.h" +#include "mxl862xx_rmon.h" + +#define MxL862XX_SDMA_PCTRLp(p) (0xBC0 + ((p) * 0x6)) +#define MXL862XX_SDMA_PCTRL_EN BIT(0) /* SDMA Port Enable */ + +/* Ethernet Switch Fetch DMA Port Control Register */ +#define MxL862XX_FDMA_PCTRLp(p) (0xA80 + ((p) * 0x6)) +#define MXL862XX_FDMA_PCTRL_EN BIT(0) /* FDMA Port Enable */ + +#pragma pack(push, 1) +#pragma scalar_storage_order little-endian + +//#define SCK_FW_1_0_41 + +/** \brief Spanning Tree Protocol port states. + Used by \ref MXL862XX_STP_port_cfg_t. */ +typedef enum { + /** Forwarding state. The port is allowed to transmit and receive + all packets. Address Learning is allowed. */ + MXL862XX_STP_PORT_STATE_FORWARD = 0, + /** Disabled/Discarding state. The port entity will not transmit + and receive any packets. Learning is disabled in this state. */ + MXL862XX_STP_PORT_STATE_DISABLE = 1, + /** Learning state. The port entity will only transmit and receive + Spanning Tree Protocol packets (BPDU). All other packets are discarded. + MAC table address learning is enabled for all good frames. */ + MXL862XX_STP_PORT_STATE_LEARNING = 2, + /** Blocking/Listening. Only the Spanning Tree Protocol packets will + be received and transmitted. All other packets are discarded by + the port entity. MAC table address learning is disabled in this + state. */ + MXL862XX_STP_PORT_STATE_BLOCKING = 3 +} mxl862xx_stp_port_state_t; + +/** \brief Bridge Port Allocation. + Used by \ref MXL862XX_BRIDGE_PORT_ALLOC and \ref MXL862XX_BRIDGE_PORT_FREE. */ +typedef struct { + /** If \ref MXL862XX_BRIDGE_PORT_ALLOC is successful, a valid ID will be returned + in this field. Otherwise, \ref INVALID_HANDLE is returned in this field. + For \ref MXL862XX_BRIDGE_PORT_FREE, this field should be valid ID returned by + \ref MXL862XX_BRIDGE_PORT_ALLOC. ID 0 is special for CPU port in PRX300 + by mapping to CTP 0 (Logical Port 0 with Sub-interface ID 0), and + pre-alloced during initialization. */ + u16 bridge_port_id; +} mxl862xx_bridge_port_alloc_t; + +/** \brief Color Remarking Mode + Used by \ref MXL862XX_CTP_port_config_t. */ +typedef enum { + /** values from last process stage */ + MXL862XX_REMARKING_NONE = 0, + /** DEI mark mode */ + MXL862XX_REMARKING_DEI = 2, + /** PCP 8P0D mark mode */ + MXL862XX_REMARKING_PCP_8P0D = 3, + /** PCP 7P1D mark mode */ + MXL862XX_REMARKING_PCP_7P1D = 4, + /** PCP 6P2D mark mode */ + MXL862XX_REMARKING_PCP_6P2D = 5, + /** PCP 5P3D mark mode */ + MXL862XX_REMARKING_PCP_5P3D = 6, + /** DSCP AF class */ + MXL862XX_REMARKING_DSCP_AF = 7 +} mxl862xx_color_remarking_mode_t; + +/** \brief Meters for various egress traffic type. + Used by \ref MXL862XX_BRIDGE_port_config_t. */ +typedef enum { + /** Index of broadcast traffic meter */ + MXL862XX_BRIDGE_PORT_EGRESS_METER_BROADCAST = 0, + /** Index of known multicast traffic meter */ + MXL862XX_BRIDGE_PORT_EGRESS_METER_MULTICAST = 1, + /** Index of unknown multicast IP traffic meter */ + MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_IP = 2, + /** Index of unknown multicast non-IP traffic meter */ + MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_NON_IP = 3, + /** Index of unknown unicast traffic meter */ + MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_UC = 4, + /** Index of traffic meter for other types */ + MXL862XX_BRIDGE_PORT_EGRESS_METER_OTHERS = 5, + /** Number of index */ + MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX = 6 +} mxl862xx_bridge_port_egress_meter_t; + +/** \brief P-mapper Mapping Mode + Used by \ref MXL862XX_CTP_port_config_t. */ +typedef enum { + /** Use PCP for VLAN tagged packets to derive sub interface ID group. + + \remarks + P-mapper table entry 1-8. */ + MXL862XX_PMAPPER_MAPPING_PCP = 0, + /** Use LAG Index for Pmapper access (regardless of IP and VLAN packet)to + derive sub interface ID group. + + \remarks + P-mapper table entry 9-72. */ + MXL862XX_PMAPPER_MAPPING_LAG = 1, + /** Use DSCP for VLAN tagged IP packets to derive sub interface ID group. + + \remarks + P-mapper table entry 9-72. */ + MXL862XX_PMAPPER_MAPPING_DSCP = 2, +} mxl862xx_pmapper_mapping_mode_t; + +/** \brief P-mapper Configuration + Used by \ref MXL862XX_CTP_port_config_t, MXL862XX_BRIDGE_port_config_t. + In case of LAG, it is user's responsibility to provide the mapped entries + in given P-mapper table. In other modes the entries are auto mapped from + input packet. */ +typedef struct { + /** Index of P-mapper <0-31>. */ + u16 pmapper_id; + + /** Sub interface ID group. + + \remarks + Entry 0 is for non-IP and non-VLAN tagged packets. Entries 1-8 are PCP + mapping entries for VLAN tagged packets with \ref MXL862XX_PMAPPER_MAPPING_PCP + selected. Entries 9-72 are DSCP or LAG mapping entries for IP packets without + VLAN tag or VLAN tagged packets with \ref MXL862XX_PMAPPER_MAPPING_DSCP or + MXL862XX_PMAPPER_MAPPING_LAG selected. When LAG is selected this 8bit field is + decoded as Destination sub-interface ID group field bits 3:0, Destination + logical port ID field bits 7:4 */ + u8 dest_sub_if_id_group[73]; +} mxl862xx_pmapper_t; + +/** \brief Bridge Port configuration mask. + Used by \ref MXL862XX_BRIDGE_port_config_t. */ +typedef enum { + /** Mask for \ref MXL862XX_BRIDGE_port_config_t::n_bridge_id */ + MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID = 0x00000001, + /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_ingress_extended_vlan_enable and + \ref MXL862XX_BRIDGE_port_config_t::n_ingress_extended_vlan_block_id */ + MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN = 0x00000002, + /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_egress_extended_vlan_enable and + \ref MXL862XX_BRIDGE_port_config_t::n_egress_extended_vlan_block_id */ + MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN = 0x00000004, + /** Mask for \ref MXL862XX_BRIDGE_port_config_t::e_ingress_marking_mode */ + MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_MARKING = 0x00000008, + /** Mask for \ref MXL862XX_BRIDGE_port_config_t::e_egress_remarking_mode */ + MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_REMARKING = 0x00000010, + /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_ingress_metering_enable and + \ref MXL862XX_BRIDGE_port_config_t::n_ingress_traffic_meter_id */ + MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_METER = 0x00000020, + /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_egress_sub_metering_enable and + \ref MXL862XX_BRIDGE_port_config_t::n_egress_traffic_sub_meter_id */ + MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_SUB_METER = 0x00000040, + /** Mask for \ref MXL862XX_BRIDGE_port_config_t::n_dest_logical_port_id, + \ref MXL862XX_BRIDGE_port_config_t::b_pmapper_enable, + \ref MXL862XX_BRIDGE_port_config_t::n_dest_sub_if_id_group, + \ref MXL862XX_BRIDGE_port_config_t::e_pmapper_mapping_mode, + \ref MXL862XX_BRIDGE_port_config_t::b_pmapper_id_valid and + \ref MXL862XX_BRIDGE_port_config_t::s_pmapper. */ + MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_CTP_MAPPING = 0x00000080, + /** Mask for \ref MXL862XX_BRIDGE_port_config_t::n_bridge_port_map */ + MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP = 0x00000100, + + /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_mc_dest_ip_lookup_enable. */ + MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_DEST_IP_LOOKUP = 0x00000200, + /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_mc_src_ip_lookup_enable. */ + MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_IP_LOOKUP = 0x00000400, + /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_dest_mac_lookup_enable. */ + MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_DEST_MAC_LOOKUP = 0x00000800, + /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_src_mac_learning_enable. */ + MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING = 0x00001000, + /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_mac_spoofing_detect_enable. */ + MXL862XX_BRIDGE_PORT_CONFIG_MASK_MAC_SPOOFING = 0x00002000, + /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_port_lock_enable. */ + MXL862XX_BRIDGE_PORT_CONFIG_MASK_PORT_LOCK = 0x00004000, + + /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_mac_learning_limit_enable and + \ref MXL862XX_BRIDGE_port_config_t::n_mac_learning_limit. */ + MXL862XX_BRIDGE_PORT_CONFIG_MASK_MAC_LEARNING_LIMIT = 0x00008000, + /** Mask for \ref MXL862XX_BRIDGE_port_config_t::n_mac_learning_count */ + MXL862XX_BRIDGE_PORT_CONFIG_MASK_MAC_LEARNED_COUNT = 0x00010000, + + /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_ingress_vlan_filter_enable and + \ref MXL862XX_BRIDGE_port_config_t::n_ingress_vlan_filter_block_id. */ + MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN_FILTER = 0x00020000, + /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_bypass_egress_vlan_filter1, + \ref MXL862XX_BRIDGE_port_config_t::b_egress_vlan_filter1Enable + and \ref MXL862XX_BRIDGE_port_config_t::n_egress_vlan_filter1Block_id. */ + MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN_FILTER1 = 0x00040000, + /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_egress_vlan_filter2Enable and + \ref MXL862XX_BRIDGE_port_config_t::n_egress_vlan_filter2Block_id. */ + MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN_FILTER2 = 0x00080000, + /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_vlan_tag_selection, + \ref MXL862XX_BRIDGE_port_config_t::b_vlan_src_mac_priority_enable, + \ref MXL862XX_BRIDGE_port_config_t::b_vlan_src_mac_dEIEnable, + \ref MXL862XX_BRIDGE_port_config_t::b_vlan_src_mac_vid_enable, + \ref MXL862XX_BRIDGE_port_config_t::b_vlan_dst_mac_priority_enable, + \ref MXL862XX_BRIDGE_port_config_t::b_vlan_dst_mac_dEIEnable, + \ref MXL862XX_BRIDGE_port_config_t::b_vlan_dst_mac_vid_enable */ + MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MAC_LEARNING = 0x00100000, + /** Mask for \ref MXL862XX_BRIDGE_port_config_t::b_vlan_multicast_priority_enable, + \ref MXL862XX_BRIDGE_port_config_t::b_vlan_multicast_dEIEnable, + \ref MXL862XX_BRIDGE_port_config_t::b_vlan_multicast_vid_enable */ + MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MULTICAST_LOOKUP = 0x00200000, + /** Mask for \ref MXL862XX_BRIDGE_port_config_t::n_loop_violation_count */ + MXL862XX_BRIDGE_PORT_CONFIG_MASK_LOOP_VIOLATION_COUNTER = 0x00400000, + /** Enable all */ + MXL862XX_BRIDGE_PORT_CONFIG_MASK_ALL = 0x7FFFFFFF, + /** Bypass any check for debug purpose */ + MXL862XX_BRIDGE_PORT_CONFIG_MASK_FORCE = 0x80000000 +} mxl862xx_bridge_port_config_mask_t; + +/** \brief Color Marking Mode + Used by \ref MXL862XX_CTP_port_config_t. */ +typedef enum { + /** mark packets (except critical) to green */ + MXL862XX_MARKING_ALL_GREEN = 0, + /** do not change color and priority */ + MXL862XX_MARKING_INTERNAL_MARKING = 1, + /** DEI mark mode */ + MXL862XX_MARKING_DEI = 2, + /** PCP 8P0D mark mode */ + MXL862XX_MARKING_PCP_8P0D = 3, + /** PCP 7P1D mark mode */ + MXL862XX_MARKING_PCP_7P1D = 4, + /** PCP 6P2D mark mode */ + MXL862XX_MARKING_PCP_6P2D = 5, + /** PCP 5P3D mark mode */ + MXL862XX_MARKING_PCP_5P3D = 6, + /** DSCP AF class */ + MXL862XX_MARKING_DSCP_AF = 7 +} mxl862xx_color_marking_mode_t; + +/** \brief Bridge Port Configuration. + Used by \ref MXL862XX_BRIDGE_PORT_CONFIG_SET and \ref MXL862XX_BRIDGE_PORT_CONFIG_GET. */ +typedef struct { + /** Bridge Port ID allocated by \ref MXL862XX_BRIDGE_PORT_ALLOC. + + \remarks + If \ref MXL862XX_BRIDGE_port_config_t::e_mask has + \ref MXL862XX_Bridge_port_config_mask_t::MXL862XX_BRIDGE_PORT_CONFIG_MASK_FORCE, this + field is absolute index of Bridge Port in hardware for debug purpose, + bypassing any check. */ + u16 bridge_port_id; + + /** Mask for updating/retrieving fields. */ + mxl862xx_bridge_port_config_mask_t mask; + + /** Bridge ID (FID) to which this bridge port is associated. A default + bridge (ID 0) should be always available. */ + u16 bridge_id; + + /** Enable extended VLAN processing for ingress non-IGMP traffic. */ + bool ingress_extended_vlan_enable; + /** Extended VLAN block allocated for ingress non-IGMP traffic. It defines + extended VLAN process for ingress non-IGMP traffic. Valid when + b_ingress_extended_vlan_enable is TRUE. */ + u16 ingress_extended_vlan_block_id; + /** Extended VLAN block size for ingress non-IGMP traffic. This is optional. + If it is 0, the block size of n_ingress_extended_vlan_block_id will be used. + Otherwise, this field will be used. */ + u16 ingress_extended_vlan_block_size; + + /** Enable extended VLAN processing enabled for egress non-IGMP traffic. */ + bool egress_extended_vlan_enable; + /** Extended VLAN block allocated for egress non-IGMP traffic. It defines + extended VLAN process for egress non-IGMP traffic. Valid when + b_egress_extended_vlan_enable is TRUE. */ + u16 egress_extended_vlan_block_id; + /** Extended VLAN block size for egress non-IGMP traffic. This is optional. + If it is 0, the block size of n_egress_extended_vlan_block_id will be used. + Otherwise, this field will be used. */ + u16 egress_extended_vlan_block_size; + + /** Ingress color marking mode for ingress traffic. */ + mxl862xx_color_marking_mode_t ingress_marking_mode; + + /** Color remarking for egress traffic. */ + mxl862xx_color_remarking_mode_t egress_remarking_mode; + + /** Traffic metering on ingress traffic applies. */ + bool ingress_metering_enable; + /** Meter for ingress Bridge Port process. + + \remarks + Meter should be allocated with \ref MXL862XX_QOS_METER_ALLOC before Bridge + port configuration. If this Bridge port is re-set, the last used meter + should be released. */ + u16 ingress_traffic_meter_id; + + /** Traffic metering on various types of egress traffic (such as broadcast, + multicast, unknown unicast, etc) applies. */ + bool egress_sub_metering_enable[MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX]; + /** Meter for egress Bridge Port process with specific type (such as + broadcast, multicast, unknown unicast, etc). Need pre-allocated for each + type. */ + u16 egress_traffic_sub_meter_id[MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX]; + + /** This field defines destination logical port. */ + u8 dest_logical_port_id; + /** This field indicates whether to enable P-mapper. */ + bool pmapper_enable; + /** When b_pmapper_enable is FALSE, this field defines destination sub + interface ID group. */ + u16 dest_sub_if_id_group; + /** When b_pmapper_enable is TRUE, this field selects either DSCP or PCP to + derive sub interface ID. */ + mxl862xx_pmapper_mapping_mode_t pmapper_mapping_mode; + /** When b_pmapper_enable is TRUE, P-mapper is used. This field determines + whether s_pmapper.n_pmapper_id is valid. If this field is TRUE, the + P-mapper is re-used and no allocation of new P-mapper or value + change in the P-mapper. If this field is FALSE, allocation is + taken care in the API implementation. */ + bool pmapper_id_valid; + /** When b_pmapper_enable is TRUE, P-mapper is used. if b_pmapper_id_valid is + FALSE, API implementation need take care of P-mapper allocation, + and maintain the reference counter of P-mapper used multiple times. + If b_pmapper_id_valid is TRUE, only s_pmapper.n_pmapper_id is used to + associate the P-mapper, and there is no allocation of new P-mapper + or value change in the P-mapper. */ + mxl862xx_pmapper_t pmapper; + + /** Port map define broadcast domain. + + \remarks + Each bit is one bridge port. Bridge port ID is index * 16 + bit offset. + For example, bit 1 of n_bridge_port_map[1] is bridge port ID 17. */ + u16 bridge_port_map[8]; /* max can be 16 */ + + /** Multicast IP table is searched if this field is FALSE and traffic is IP + multicast. */ + bool mc_dest_ip_lookup_disable; + /** Multicast IP table is searched if this field is TRUE and traffic is IP + multicast. */ + bool mc_src_ip_lookup_enable; + + /** Default is FALSE. Packet is treated as "unknown" if it's not + broadcast/multicast packet. */ + bool dest_mac_lookup_disable; + + /** Default is FALSE. Source MAC address is learned. */ + bool src_mac_learning_disable; + + /** If this field is TRUE and MAC address which is already learned in another + bridge port appears on this bridge port, port locking violation is + detected. */ + bool mac_spoofing_detect_enable; + + /** If this field is TRUE and MAC address which is already learned in this + bridge port appears on another bridge port, port locking violation is + detected. */ + bool port_lock_enable; + + /** Enable MAC learning limitation. */ + bool mac_learning_limit_enable; + /** Max number of MAC can be learned from this bridge port. */ + u16 mac_learning_limit; + + /** Get number of Loop violation counter from this bridge port. */ + u16 loop_violation_count; + + /** Get number of MAC address learned from this bridge port. */ + u16 mac_learning_count; + + /** Enable ingress VLAN filter */ + bool ingress_vlan_filter_enable; + /** VLAN filter block of ingress traffic if + \ref MXL862XX_BRIDGE_port_config_t::b_ingress_vlan_filter_enable is TRUE. */ + u16 ingress_vlan_filter_block_id; + /** VLAN filter block size. This is optional. + If it is 0, the block size of n_ingress_vlan_filter_block_id will be used. + Otherwise, this field will be used. */ + u16 ingress_vlan_filter_block_size; + /** For ingress traffic, bypass VLAN filter 1 at egress bridge port + processing. */ + bool bypass_egress_vlan_filter1; + /** Enable egress VLAN filter 1 */ + bool egress_vlan_filter1enable; + /** VLAN filter block 1 of egress traffic if + \ref MXL862XX_BRIDGE_port_config_t::b_egress_vlan_filter1Enable is TRUE. */ + u16 egress_vlan_filter1block_id; + /** VLAN filter block 1 size. This is optional. + If it is 0, the block size of n_egress_vlan_filter1Block_id will be used. + Otherwise, this field will be used. */ + u16 egress_vlan_filter1block_size; + /** Enable egress VLAN filter 2 */ + bool egress_vlan_filter2enable; + /** VLAN filter block 2 of egress traffic if + \ref MXL862XX_BRIDGE_port_config_t::b_egress_vlan_filter2Enable is TRUE. */ + u16 egress_vlan_filter2block_id; + /** VLAN filter block 2 size. This is optional. + If it is 0, the block size of n_egress_vlan_filter2Block_id will be used. + Otherwise, this field will be used. */ + u16 egress_vlan_filter2block_size; +#ifdef SCK_FW_1_0_41 + /** Enable Ingress VLAN Based Mac Learning */ + bool ingress_vlan_based_mac_learning_enable; + /** 0 - Intermediate outer VLAN + tag is used for MAC address/multicast + learning, lookup and filtering. + 1 - Original outer VLAN tag is used + for MAC address/multicast learning, lookup + and filtering. */ +#endif + bool vlan_tag_selection; + /** 0 - Disable, VLAN Priority field is not used + and value 0 is used for source MAC address + learning and filtering. + 1 - Enable, VLAN Priority field is used for + source MAC address learning and filtering. */ + bool vlan_src_mac_priority_enable; + /** 0 - Disable, VLAN DEI/CFI field is not used + and value 0 is used for source MAC address + learning and filtering. + 1 - Enable, VLAN DEI/CFI field is used for + source MAC address learning and filtering */ + bool vlan_src_mac_dei_enable; + /** 0 - Disable, VLAN ID field is not used and + value 0 is used for source MAC address + learning and filtering + 1 - Enable, VLAN ID field is used for source + MAC address learning and filtering. */ + bool vlan_src_mac_vid_enable; + /** 0 - Disable, VLAN Priority field is not used + and value 0 is used for destination MAC + address look up and filtering. + 1 - Enable, VLAN Priority field is used for + destination MAC address look up and + filtering */ + bool vlan_dst_mac_priority_enable; + /** 0 - Disable, VLAN CFI/DEI field is not used + and value 0 is used for destination MAC + address lookup and filtering. + 1 - Enable, VLAN CFI/DEI field is used for + destination MAC address look up and + filtering. */ + bool vlan_dst_mac_dei_enable; + /** 0 - Disable, VLAN ID field is not used and + value 0 is used for destination MAC address + look up and filtering. + 1 - Enable, VLAN ID field is destination for + destination MAC address look up and + filtering. */ + bool vlan_dst_mac_vid_enable; +#ifdef SCK_FW_1_0_41 + /** Enable, VLAN Based Multicast Lookup */ + bool vlan_based_multi_cast_lookup; + /** 0 - Disable, VLAN Priority field is not used + and value 0 is used for IP multicast lookup. + 1 - Enable, VLAN Priority field is used for IP + multicast lookup. */ +#endif + bool vlan_multicast_priority_enable; + /** 0 - Disable, VLAN CFI/DEI field is not used + and value 0 is used for IP multicast lookup. + 1 - Enable, VLAN CFI/DEI field is used for IP + multicast lookup. */ + bool vlan_multicast_dei_enable; + /** 0 - Disable, VLAN ID field is not used and + value 0 is used for IP multicast lookup. + 1 - Enable, VLAN ID field is destination for IP + multicast lookup. */ + bool vlan_multicast_vid_enable; +} mxl862xx_bridge_port_config_t; + +/** \brief Bridge Allocation. + Used by \ref MXL862XX_BRIDGE_ALLOC and \ref MXL862XX_BRIDGE_FREE. */ +typedef struct { + /** If \ref MXL862XX_BRIDGE_ALLOC is successful, a valid ID will be returned + in this field. Otherwise, \ref INVALID_HANDLE is returned in this field. + For \ref MXL862XX_BRIDGE_FREE, this field should be valid ID returned by + \ref MXL862XX_BRIDGE_ALLOC. ID 0 is special Bridge created during + initialization. */ + u16 bridge_id; +} mxl862xx_bridge_alloc_t; + +/** \brief Ethernet port speed mode. + For certain generations of GSWIP, a port might support only a subset of the possible settings. + Used by \ref MXL862XX_port_link_cfg_t. */ +typedef enum { + /** 10 Mbit/s */ + MXL862XX_PORT_SPEED_10, + /** 100 Mbit/s */ + MXL862XX_PORT_SPEED_100, + /** 200 Mbit/s */ + MXL862XX_PORT_SPEED_200, + /** 1000 Mbit/s */ + MXL862XX_PORT_SPEED_1000, + /** 2.5 Gbit/s */ + MXL862XX_PORT_SPEED_2500, + /** 5 Gbit/s */ + MXL862XX_PORT_SPEED_5000, + /** 10 Gbit/s */ + MXL862XX_PORT_SPEED_10000, + /** Auto speed for XGMAC */ + MXL862XX_PORT_SPEED_AUTO, +} mxl862xx_port_speed_t; + +/** \brief Ethernet port duplex status. + Used by \ref MXL862XX_port_link_cfg_t. */ +typedef enum { + /** Port operates in full-duplex mode */ + MXL862XX_DUPLEX_FULL = 0, + /** Port operates in half-duplex mode */ + MXL862XX_DUPLEX_HALF = 1, + /** Port operates in Auto mode */ + MXL862XX_DUPLEX_AUTO = 2, +} mxl862xx_port_duplex_t; + +/** \brief Force the MAC and PHY link modus. + Used by \ref MXL862XX_port_link_cfg_t. */ +typedef enum { + /** Link up. Any connected LED + still behaves based on the real PHY status. */ + MXL862XX_PORT_LINK_UP = 0, + /** Link down. */ + MXL862XX_PORT_LINK_DOWN = 1, + /** Link Auto. */ + MXL862XX_PORT_LINK_AUTO = 2, +} mxl862xx_port_link_t; + +/** \brief Ethernet port interface mode. + A port might support only a subset of the possible settings. + Used by \ref MXL862XX_port_link_cfg_t. */ +typedef enum { + /** Normal PHY interface (twisted pair), use the internal MII Interface. */ + MXL862XX_PORT_HW_MII = 0, + /** Reduced MII interface in normal mode. */ + MXL862XX_PORT_HW_RMII = 1, + /** GMII or MII, depending upon the speed. */ + MXL862XX_PORT_HW_GMII = 2, + /** RGMII mode. */ + MXL862XX_PORT_HW_RGMII = 3, + /** XGMII mode. */ + MXL862XX_PORT_HW_XGMII = 4, +} mxl862xx_mii_mode_t; + +/** \brief Ethernet port configuration for PHY or MAC mode. + Used by \ref MXL862XX_port_link_cfg_t. */ +typedef enum { + /** MAC Mode. The Ethernet port is configured to work in MAC mode. */ + MXL862XX_PORT_MAC = 0, + /** PHY Mode. The Ethernet port is configured to work in PHY mode. */ + MXL862XX_PORT_PHY = 1 +} mxl862xx_mii_type_t; + +/** \brief Ethernet port clock source configuration. + Used by \ref MXL862XX_port_link_cfg_t. */ +typedef enum { + /** Clock Mode not applicable. */ + MXL862XX_PORT_CLK_NA = 0, + /** Clock Master Mode. The port is configured to provide the clock as output signal. */ + MXL862XX_PORT_CLK_MASTER = 1, + /** Clock Slave Mode. The port is configured to use the input clock signal. */ + MXL862XX_PORT_CLK_SLAVE = 2 +} mxl862xx_clk_mode_t; + +/** \brief Ethernet port link, speed status and flow control status. + Used by \ref MXL862XX_PORT_LINK_CFG_GET and \ref MXL862XX_PORT_LINK_CFG_SET. */ +typedef struct { + /** Ethernet Port number (zero-based counting). The valid range is hardware + dependent. An error code is delivered if the selected port is not + available. */ + u16 port_id; + /** Force Port Duplex Mode. + + - 0: Negotiate Duplex Mode. Auto-negotiation mode. Negotiated + duplex mode given in 'e_duplex' + during MXL862XX_PORT_LINK_CFG_GET calls. + - 1: Force Duplex Mode. Force duplex mode in 'e_duplex'. + */ + bool duplex_force; + /** Port Duplex Status. */ + mxl862xx_port_duplex_t duplex; + /** Force Link Speed. + + - 0: Negotiate Link Speed. Negotiated speed given in + 'e_speed' during MXL862XX_PORT_LINK_CFG_GET calls. + - 1: Force Link Speed. Forced speed mode in 'e_speed'. + */ + bool speed_force; + /** Ethernet port link up/down and speed status. */ + mxl862xx_port_speed_t speed; + /** Force Link. + + - 0: Auto-negotiate Link. Current link status is given in + 'e_link' during MXL862XX_PORT_LINK_CFG_GET calls. + - 1: Force Duplex Mode. Force duplex mode in 'e_link'. + */ + bool link_force; + /** Link Status. Read out the current link status. + Note that the link could be forced by setting 'b_link_force'. */ + mxl862xx_port_link_t link; + /** Selected interface mode (MII/RMII/RGMII/GMII/XGMII). */ + mxl862xx_mii_mode_t mii_mode; + /** Select MAC or PHY mode (PHY = Reverse x_mII). */ + mxl862xx_mii_type_t mii_type; + /** Interface Clock mode (used for RMII mode). */ + mxl862xx_clk_mode_t clk_mode; + /** 'Low Power Idle' Support for 'Energy Efficient Ethernet'. + Only enable this feature in case the attached PHY also supports it. */ + bool lpi; +} mxl862xx_port_link_cfg_t; + +/** \brief Port Enable Type Selection. + Used by \ref MXL862XX_port_cfg_t. */ +typedef enum { + /** The port is disabled in both directions. */ + MXL862XX_PORT_DISABLE = 0, + /** The port is enabled in both directions (ingress and egress). */ + MXL862XX_PORT_ENABLE_RXTX = 1, + /** The port is enabled in the receive (ingress) direction only. */ + MXL862XX_PORT_ENABLE_RX = 2, + /** The port is enabled in the transmit (egress) direction only. */ + MXL862XX_PORT_ENABLE_TX = 3 +} mxl862xx_port_enable_t; + +/** \brief Ethernet flow control status. + Used by \ref MXL862XX_port_cfg_t. */ +typedef enum { + /** Automatic flow control mode selection through auto-negotiation. */ + MXL862XX_FLOW_AUTO = 0, + /** Receive flow control only */ + MXL862XX_FLOW_RX = 1, + /** Transmit flow control only */ + MXL862XX_FLOW_TX = 2, + /** Receive and Transmit flow control */ + MXL862XX_FLOW_RXTX = 3, + /** No flow control */ + MXL862XX_FLOW_OFF = 4 +} mxl862xx_port_flow_t; + +/** \brief Port Mirror Options. + Used by \ref MXL862XX_port_cfg_t. */ +typedef enum { + /** Mirror Feature is disabled. Normal port usage. */ + MXL862XX_PORT_MONITOR_NONE = 0, + /** Port Ingress packets are mirrored to the monitor port. */ + MXL862XX_PORT_MONITOR_RX = 1, + /** Port Egress packets are mirrored to the monitor port. */ + MXL862XX_PORT_MONITOR_TX = 2, + /** Port Ingress and Egress packets are mirrored to the monitor port. */ + MXL862XX_PORT_MONITOR_RXTX = 3, + /** Packet mirroring of 'unknown VLAN violation' frames. */ + MXL862XX_PORT_MONITOR_VLAN_UNKNOWN = 4, + /** Packet mirroring of 'VLAN ingress or egress membership violation' frames. */ + MXL862XX_PORT_MONITOR_VLAN_MEMBERSHIP = 16, + /** Packet mirroring of 'port state violation' frames. */ + MXL862XX_PORT_MONITOR_PORT_STATE = 32, + /** Packet mirroring of 'MAC learning limit violation' frames. */ + MXL862XX_PORT_MONITOR_LEARNING_LIMIT = 64, + /** Packet mirroring of 'port lock violation' frames. */ + MXL862XX_PORT_MONITOR_PORT_LOCK = 128 +} mxl862xx_port_monitor_t; + +/** \brief Interface RMON Counter Mode - (FID, SUBID or FLOWID) Config - GSWIP-3.0 only. + Used by \ref MXL862XX_port_cfg_t. */ +typedef enum { + /** FID based Interface RMON counters Usage */ + MXL862XX_IF_RMON_FID = 0, + /** Sub-Interface Id based Interface RMON counters Usage */ + MXL862XX_IF_RMON_SUBID = 1, + /** Flow Id (LSB bits 3 to 0) based Interface RMON counters Usage */ + MXL862XX_IF_RMON_FLOWID_LSB = 2, + /** Flow Id (MSB bits 7 to 4) based Interface RMON counters Usage */ + MXL862XX_IF_RMON_FLOWID_MSB = 3 +} mxl862xx_if_rmon_mode_t; + +/** \brief Port Configuration Parameters. + Used by \ref MXL862XX_PORT_CFG_GET and \ref MXL862XX_PORT_CFG_SET. */ +typedef struct { + /** Port Type. This gives information which type of port is configured. + n_port_id should be based on this field. */ + mxl862xx_port_type_t port_type; + + /** Ethernet Port number (zero-based counting). The valid range is hardware + dependent. An error code is delivered if the selected port is not + available. */ + u16 port_id; + /** Enable Port (ingress only, egress only, both directions, or disabled). + This parameter is used for Spanning Tree Protocol and 802.1X applications. */ + mxl862xx_port_enable_t enable; + /** Drop unknown unicast packets. + Do not send out unknown unicast packets on this port, + if the boolean parameter is enabled. By default packets of this type + are forwarded to this port. */ + bool unicast_unknown_drop; + /** Drop unknown multicast packets. + Do not send out unknown multicast packets on this port, + if boolean parameter is enabled. By default packets of this type + are forwarded to this port. + Some platforms also drop broadcast packets. */ + bool multicast_unknown_drop; + /** Drop reserved packet types + (destination address from '01 80 C2 00 00 00' to + '01 80 C2 00 00 2F') received on this port. */ + bool reserved_packet_drop; + /** Drop Broadcast packets received on this port. By default packets of this + type are forwarded to this port. */ + bool broadcast_drop; + /** Enables MAC address table aging. + The MAC table entries learned on this port are removed after the + aging time has expired. + The aging time is a global parameter, common to all ports. */ + bool aging; + /** MAC address table learning on the port specified by 'n_port_id'. + By default this parameter is always enabled. */ + bool learning; + /** Automatic MAC address table learning locking on the port specified + by 'n_port_id'. + This parameter is only taken into account when 'b_learning' is enabled. */ + bool learning_mac_port_lock; + /** Automatic MAC address table learning limitation on this port. + The learning functionality is disabled when the limit value is zero. + The value 0x_fFFF to allow unlimited learned address. + This parameter is only taken into account when 'b_learning' is enabled. */ + u16 learning_limit; + /** MAC spoofing detection. Identifies ingress packets that carry + a MAC source address which was previously learned on a different ingress + port (learned by MAC bridging table). This also applies to static added + entries. Those violated packets could be accepted or discarded, + depending on the global switch configuration 'b_mAC_Spoofing_action'. + This parameter is only taken into account when 'b_learning' is enabled. */ + bool mac_spoofing_detection; + /** Port Flow Control Status. Enables the flow control function. */ + mxl862xx_port_flow_t flow_ctrl; + /** Port monitor feature. Allows forwarding of egress and/or ingress + packets to the monitor port. If enabled, the monitor port gets + a copy of the selected packet type. */ + mxl862xx_port_monitor_t port_monitor; + /** Assign Interface RMON Counters for this Port - GSWIP-3.0 */ + bool if_counters; + /** Interface RMON Counters Start Index - GSWIP-3.0. + Value of (-1) denotes unassigned Interface Counters. + Valid range : 0-255 available to be shared amongst ports in desired way*/ + int if_count_start_idx; + /** Interface RMON Counters Mode - GSWIP-3.0 */ + mxl862xx_if_rmon_mode_t if_rmonmode; +} mxl862xx_port_cfg_t; + +#define MXL862XX_ERROR_BASE 1024 +/** \brief Enumeration for function status return. The upper four bits are reserved for error classification */ +typedef enum { + /** Correct or Expected Status */ + MXL862XX_STATUS_OK = 0, + /** Generic or unknown error occurred */ + MXL862XX_STATUS_ERR = -1, + /** Invalid function parameter */ + MXL862XX_STATUS_PARAM = -(MXL862XX_ERROR_BASE + 2), + /** No space left in VLAN table */ + MXL862XX_STATUS_VLAN_SPACE = -(MXL862XX_ERROR_BASE + 3), + /** Requested VLAN ID not found in table */ + MXL862XX_STATUS_VLAN_ID = -(MXL862XX_ERROR_BASE + 4), + /** Invalid ioctl */ + MXL862XX_STATUS_INVAL_IOCTL = -(MXL862XX_ERROR_BASE + 5), + /** Operation not supported by hardware */ + MXL862XX_STATUS_NO_SUPPORT = -(MXL862XX_ERROR_BASE + 6), + /** Timeout */ + MXL862XX_STATUS_TIMEOUT = -(MXL862XX_ERROR_BASE + 7), + /** At least one value is out of range */ + MXL862XX_STATUS_VALUE_RANGE = -(MXL862XX_ERROR_BASE + 8), + /** The port_id/queue_id/meter_id/etc. is not available in this hardware or the + selected feature is not available on this port */ + MXL862XX_STATUS_PORT_INVALID = -(MXL862XX_ERROR_BASE + 9), + /** The interrupt is not available in this hardware */ + MXL862XX_STATUS_IRQ_INVALID = -(MXL862XX_ERROR_BASE + 10), + /** The MAC table is full, an entry could not be added */ + MXL862XX_STATUS_MAC_TABLE_FULL = -(MXL862XX_ERROR_BASE + 11), + /** Locking failed - SWAPI is busy */ + MXL862XX_STATUS_LOCK_FAILED = -(MXL862XX_ERROR_BASE + 12), + /** Multicast Forwarding table entry not found */ + MXL862XX_STATUS_ENTRY_NOT_FOUND = -(MXL862XX_ERROR_BASE + 13), +} mxl862xx_return_t; + +/** \brief MAC Table Clear Type + Used by \ref MXL862XX_MAC_table_clear_cond_t */ +typedef enum { + /** Clear dynamic entries based on Physical Port */ + MXL862XX_MAC_CLEAR_PHY_PORT = 0, + /** Clear all dynamic entries */ + MXL862XX_MAC_CLEAR_DYNAMIC = 1, +} mxl862xx_mac_clear_type_t; + +/** \brief MAC Table Clear based on given condition. + Used by \ref MXL862XX_MAC_TABLE_CLEAR_COND. */ +typedef struct { + /** MAC table clear type \ref MXL862XX_Mac_clear_type_t */ + u8 type; + union { + /** Physical port id (0~16) if \ref e_type is + ref MXL862XX_MAC_CLEAR_PHY_PORT. */ + u8 port_id; + }; +} mxl862xx_mac_table_clear_cond_t; + +/** \brief Configures the Spanning Tree Protocol state of an Ethernet port. + Used by \ref MXL862XX_STP_PORT_CFG_SET + and \ref MXL862XX_STP_PORT_CFG_GET. */ +typedef struct { + /** Ethernet Port number (zero-based counting) in GSWIP-2.1/2.2/3.0. From + GSWIP-3.1, this field is Bridge Port ID. The valid range is hardware + dependent. An error code is delivered if the selected port is not + available. */ + u16 port_id; + /** Filtering Identifier (FID) (not supported by all switches). + The FID allows to keep multiple STP states per physical Ethernet port. + Multiple CTAG VLAN groups could be a assigned to one FID and therefore + share the same STP port state. Switch API ignores the FID value + in case the switch device does not support it or switch CTAG VLAN + awareness is disabled. */ + u16 fid; + /** Spanning Tree Protocol state of the port. */ + mxl862xx_stp_port_state_t port_state; +} mxl862xx_stp_port_cfg_t; + +/** \brief Special tag Ethertype mode */ +typedef enum { + /** The ether_type field of the Special Tag of egress packets is always set + to a prefined value. This same defined value applies for all + switch ports. */ + MXL862XX_CPU_ETHTYPE_PREDEFINED = 0, + /** The Ethertype field of the Special Tag of egress packets is set to + the flow_iD parameter, which is a results of the switch flow + classification result. The switch flow table rule provides this + flow_iD as action parameter. */ + MXL862XX_CPU_ETHTYPE_FLOWID = 1 +} mxl862xx_cpu_special_tag_eth_type_t; + +/** \brief Parser Flags and Offsets Header settings on CPU Port for GSWIP-3.0. + Used by \ref MXL862XX_CPU_Port_cfg_t. */ +typedef enum { + /** No Parsing Flags or Offsets accompanying to CPU Port for this combination */ + MXL862XX_CPU_PARSER_NIL = 0, + /** 8-Bytes Parsing Flags (Bit 63:0) accompanying to CPU Port for this combination */ + MXL862XX_CPU_PARSER_FLAGS = 1, + /** 40-Bytes Offsets (Offset-0 to -39) + 8-Bytes Parsing Flags (Bit 63:0) accompanying to CPU Port for this combination */ + MXL862XX_CPU_PARSER_OFFSETS_FLAGS = 2, + /** Reserved - for future use */ + MXL862XX_CPU_PARSER_RESERVED = 3 +} mxl862xx_cpu_parser_header_cfg_t; + +/** \brief FCS and Pad Insertion operations for GSWIP 3.1 + Used by \ref MXL862XX_CPU_Port_cfg_set/Get. */ +typedef enum { + /** CRC Pad Insertion Enable */ + MXL862XX_CRC_PAD_INS_EN = 0, + /** CRC Insertion Enable Pad Insertion Disable */ + MXL862XX_CRC_EN_PAD_DIS = 1, + /** CRC Pad Insertion Disable */ + MXL862XX_CRC_PAD_INS_DIS = 2 +} mxl862xx_fcs_tx_ops_t; + +/** \brief Defines one port that is directly connected to the CPU and its applicable settings. + Used by \ref MXL862XX_CPU_PORT_CFG_SET and \ref MXL862XX_CPU_PORT_CFG_GET. */ +typedef struct { + /** Ethernet Port number (zero-based counting) set to CPU Port. The valid number is hardware + dependent. (E.g. Port number 0 for GSWIP-3.0 or 6 for GSWIP-2.x). An error code is delivered if the selected port is not + available. */ + u16 port_id; + /** CPU port validity. + Set command: set true to define a CPU port, set false to undo the setting. + Get command: true if defined as CPU, false if not defined as CPU port. */ + bool cPU_Port_valid; + /** Special tag enable in ingress direction. */ + bool special_tag_ingress; + /** Special tag enable in egress direction. */ + bool special_tag_egress; + /** Enable FCS check + + - false: No check, forward all frames + - 1: Check FCS, drop frames with errors + */ + bool fcs_check; + /** Enable FCS generation + + - false: Forward packets without FCS + - 1: Generate FCS for all frames + */ + bool fcs_generate; + /** Special tag Ethertype mode. Not applicable to GSWIP-3.1. */ + mxl862xx_cpu_special_tag_eth_type_t special_tag_eth_type; + /** GSWIP-3.0 specific Parser Header Config for no MPE flags (i.e. MPE1=0, MPE2=0). */ + mxl862xx_cpu_parser_header_cfg_t no_mpeparser_cfg; + /** GSWIP-3.0 specific Parser Header Config for MPE-1 set flag (i.e. MPE1=1, MPE2=0). */ + mxl862xx_cpu_parser_header_cfg_t mpe1parser_cfg; + /** GSWIP-3.0 specific Parser Header Config for MPE-2 set flag (i.e. MPE1=0, MPE2=1). */ + mxl862xx_cpu_parser_header_cfg_t mpe2parser_cfg; + /** GSWIP-3.0 specific Parser Header Config for both MPE-1 and MPE-2 set flag (i.e. MPE1=1, MPE2=1). */ + mxl862xx_cpu_parser_header_cfg_t mpe1mpe2parser_cfg; + /** GSWIP-3.1 FCS tx Operations. */ + mxl862xx_fcs_tx_ops_t fcs_tx_ops; + /** GSWIP-3.2 Time Stamp Field Removal for PTP Packet + 0 - DIS Removal is disabled + 1 - EN Removal is enabled + */ + bool ts_ptp; + /** GSWIP-3.2 Time Stamp Field Removal for Non-PTP Packet + 0 - DIS Removal is disabled + 1 - EN Removal is enabled + */ + bool ts_nonptp; +} mxl862xx_cpu_port_cfg_t; + +typedef struct { + /* port ID (1~16) */ + u8 pid; + + /* bit value 1 to indicate valid field + * bit 0 - rx + * 1 - tx + * 2 - rx_pen + * 3 - tx_pen + */ + u8 mask; + + /* RX special tag mode + * value 0 - packet does NOT have special tag and special tag is + * NOT inserted + * 1 - packet does NOT have special tag and special tag is + * inserted + * 2 - packet has special tag and special tag is NOT inserted + */ + u8 rx; + + /* TX special tag mode + * value 0 - packet does NOT have special tag and special tag is + * NOT removed + * 1 - packet has special tag and special tag is replaced + * 2 - packet has special tag and special tag is NOT removed + * 3 - packet has special tag and special tag is removed + */ + u8 tx; + + /* RX special tag info over preamble + * value 0 - special tag info inserted from byte 2 to 7 are all 0 + * 1 - special tag byte 5 is 16, other bytes from 2 to 7 are 0 + * 2 - special tag byte 5 is from preamble field, others are 0 + * 3 - special tag byte 2 to 7 are from preabmle field + */ + u8 rx_pen; + + /* TX special tag info over preamble + * value 0 - disabled + * 1 - enabled + */ + u8 tx_pen; +} mxl862xx_ss_sp_tag_t; + +/** \brief Port monitor configuration. + Used by \ref MXL862XX_MONITOR_PORT_CFG_GET and \ref MXL862XX_MONITOR_PORT_CFG_SET. */ +typedef struct { + /** Ethernet Port number (zero-based counting). The valid range is hardware + dependent. An error code is delivered if the selected port is not + available. */ + u8 port_id; + /* Monitoring Sub-IF id */ + u16 sub_if_id; + /* Out of use. */ + bool monitor_port; +} mxl862xx_monitor_port_cfg_t; + +/** \brief VLAN Filter TCI Mask. + Used by \ref MXL862XX_VLANFILTER_config_t */ +typedef enum { + MXL862XX_VLAN_FILTER_TCI_MASK_VID = 0, + MXL862XX_VLAN_FILTER_TCI_MASK_PCP = 1, + MXL862XX_VLAN_FILTER_TCI_MASK_TCI = 2 +} mxl862xx_vlan_filter_tci_mask_t; + +typedef enum { + MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_1 = 0, + MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_2 = 1, + MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_3 = 2, + MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_4 = 3 +} mxl862xx_extended_vlan_4_tpid_mode_t; + +/** \brief Extended VLAN Filter TPID Field. + Used by \ref MXL862XX_EXTENDEDVLAN_filter_vLAN_t. */ +typedef enum { + /** Do not filter. */ + MXL862XX_EXTENDEDVLAN_FILTER_TPID_NO_FILTER = 0, + /** TPID is 0x8100. */ + MXL862XX_EXTENDEDVLAN_FILTER_TPID_8021Q = 1, + /** TPID is global configured value. */ + MXL862XX_EXTENDEDVLAN_FILTER_TPID_VTETYPE = 2 +} mxl862xx_extended_vlan_filter_tpid_t; + +/** \brief Extended VLAN Treatment Set TPID. + Used by \ref MXL862XX_EXTENDEDVLAN_treatment_vlan_t. */ +typedef enum { + /** TPID is copied from inner VLAN tag of received packet. */ + MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_TPID = 0, + /** TPID is copied from outer VLAN tag of received packet. */ + MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_TPID = 1, + /** TPID is global configured value. */ + MXL862XX_EXTENDEDVLAN_TREATMENT_VTETYPE = 2, + /** TPID is 0x8100. */ + MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q = 3 +} mxl862xx_extended_vlan_treatment_tpid_t; + +/** \brief Extended VLAN Filter DEI Field. + Used by \ref MXL862XX_EXTENDEDVLAN_filter_vLAN_t. */ +typedef enum { + /** Do not filter. */ + MXL862XX_EXTENDEDVLAN_FILTER_DEI_NO_FILTER = 0, + /** DEI is 0. */ + MXL862XX_EXTENDEDVLAN_FILTER_DEI_0 = 1, + /** DEI is 1. */ + MXL862XX_EXTENDEDVLAN_FILTER_DEI_1 = 2 +} mxl862xx_extended_vlan_filter_dei_t; + +/** \brief Extended VLAN Treatment Set DEI. + Used by \ref MXL862XX_EXTENDEDVLAN_treatment_vlan_t. */ +typedef enum { + /** DEI (if applicable) is copied from inner VLAN tag of received packet. */ + MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_DEI = 0, + /** DEI (if applicable) is copied from outer VLAN tag of received packet. */ + MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_DEI = 1, + /** DEI is 0. */ + MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0 = 2, + /** DEI is 1. */ + MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_1 = 3 +} mxl862xx_extended_vlan_treatment_dei_t; + +/** \brief Extended VLAN Filter Type. + Used by \ref MXL862XX_EXTENDEDVLAN_filter_vLAN_t. */ +typedef enum { + /** There is tag and criteria applies. */ + MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL = 0, + /** There is tag but no criteria. */ + MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER = 1, + /** Default entry if no other rule applies. */ + MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT = 2, + /** There is no tag. */ + MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG = 3, + /** Block invalid*/ + MXL862XX_EXTENDEDVLAN_BLOCK_INVALID = 4 +} mxl862xx_extended_vlan_filter_type_t; + +/** \brief Extended VLAN Filter ether_type. + Used by \ref MXL862XX_EXTENDEDVLAN_filter_vLAN_t. */ +typedef enum { + /** Do not filter. */ + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_NO_FILTER = 0, + /** IPo_e frame (Ethertyp is 0x0800). */ + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_IPOE = 1, + /** PPPo_e frame (Ethertyp is 0x8863 or 0x8864). */ + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_PPPOE = 2, + /** ARP frame (Ethertyp is 0x0806). */ + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_ARP = 3, + /** IPv6 IPo_e frame (Ethertyp is 0x86DD). */ + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_IPV6IPOE = 4, + /** EAPOL (Ethertyp is 0x888E). */ + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_EAPOL = 5, + /** DHCPV4 (UDP DESTINATION PORT 67&68). */ + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_DHCPV4 = 6, + /** DHCPV6 (UDP DESTINATION PORT 546&547). */ + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_DHCPV6 = 7 +} mxl862xx_extended_vlan_filter_ethertype_t; + +/** \brief Extended VLAN Treatment Set Priority. + Used by \ref MXL862XX_EXTENDEDVLAN_treatment_vlan_t. */ +typedef enum { + /** Set priority with given value. */ + MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL = 0, + /** Prority value is copied from inner VLAN tag of received packet. */ + MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_PRORITY = 1, + /** Prority value is copied from outer VLAN tag of received packet. */ + MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_PRORITY = 2, + /** Prority value is derived from DSCP field of received packet. */ + MXL862XX_EXTENDEDVLAN_TREATMENT_DSCP = 3 +} mxl862xx_extended_vlan_treatment_priority_t; + +/** \brief Extended VLAN Treatment Set VID. + Used by \ref MXL862XX_EXTENDEDVLAN_treatment_vlan_t. */ +typedef enum { + /** Set VID with given value. */ + MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL = 0, + /** VID is copied from inner VLAN tag of received packet. */ + MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_VID = 1, + /** VID is copied from outer VLAN tag of received packet. */ + MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_VID = 2, +} mxl862xx_extended_vlan_treatment_vid_t; + +/** \brief Extended VLAN Treatment Remove Tag. + Used by \ref MXL862XX_EXTENDEDVLAN_treatment_vlan_t. */ +typedef enum { + /** Do not remove VLAN tag. */ + MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG = 0, + /** Remove 1 VLAN tag following DA/SA. */ + MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG = 1, + /** Remove 2 VLAN tag following DA/SA. */ + MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_2_TAG = 2, + /** Discard upstream traffic. */ + MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM = 3, +} mxl862xx_extended_vlan_treatment_remove_tag_t; + +/** \brief Extended VLAN Filter VLAN Tag. + Used by \ref MXL862XX_EXTENDEDVLAN_filter_t. */ +typedef struct { + /** Filter Type: normal filter, default rule, or no tag */ + mxl862xx_extended_vlan_filter_type_t type; + /** Enable priority field filtering. */ + bool priority_enable; + /** Filter priority value if b_priority_enable is TRUE. */ + u32 priority_val; + /** Enable VID filtering. */ + bool vid_enable; + /** Filter VID if b_vid_enable is TRUE. */ + u32 vid_val; + /** Mode to filter TPID of VLAN tag. */ + mxl862xx_extended_vlan_filter_tpid_t tpid; + /** Mode to filter DEI of VLAN tag. */ + mxl862xx_extended_vlan_filter_dei_t dei; +} mxl862xx_extendedvlan_filter_vlan_t; + +/** \brief Extended VLAN Treatment VLAN Tag. + Used by \ref MXL862XX_EXTENDEDVLAN_treatment_t. */ +typedef struct { + /** Select source of priority field of VLAN tag. */ + mxl862xx_extended_vlan_treatment_priority_t priority_mode; + /** If \ref MXL862XX_EXTENDEDVLAN_treatment_vlan_t::priority_mode is + \ref MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL, use this value for + priority field of VLAN tag. */ + u32 priority_val; + /** Select source of VID field of VLAN tag. */ + mxl862xx_extended_vlan_treatment_vid_t vid_mode; + /** If \ref MXL862XX_EXTENDEDVLAN_treatment_vlan_t::e_vid_mode is + \ref MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL, use this value for VID field + of VLAN tag. */ + u32 vid_val; + /** Select source of TPID field of VLAN tag. */ + mxl862xx_extended_vlan_treatment_tpid_t tpid; + /** Select source of DEI field of VLAN tag. */ + mxl862xx_extended_vlan_treatment_dei_t dei; +} mxl862xx_extendedvlan_treatment_vlan_t; + +/** \brief Extended VLAN Filter. + Used by \ref MXL862XX_EXTENDEDVLAN_config_t. */ +typedef struct { + /** Filter on Original Packet. */ + bool original_packet_filter_mode; + mxl862xx_extended_vlan_4_tpid_mode_t filter_4_tpid_mode; + /** Filter for outer VLAN tag. */ + mxl862xx_extendedvlan_filter_vlan_t outer_vlan; + /** Filter for inner VLAN tag. */ + mxl862xx_extendedvlan_filter_vlan_t inner_vlan; + /** Filter ether_type. */ + mxl862xx_extended_vlan_filter_ethertype_t ether_type; +} mxl862xx_extendedvlan_filter_t; + +/** \brief Extended VLAN Allocation. + Used by \ref MXL862XX_EXTENDEDVLAN_ALLOC and \ref MXL862XX_EXTENDEDVLAN_FREE. */ +typedef struct { + /** Total number of extended VLAN entries are requested. Proper value should + be given for \ref MXL862XX_EXTENDEDVLAN_ALLOC. This field is ignored for + \ref MXL862XX_EXTENDEDVLAN_FREE. */ + u16 number_of_entries; + + /** If \ref MXL862XX_EXTENDEDVLAN_ALLOC is successful, a valid ID will be returned + in this field. Otherwise, \ref INVALID_HANDLE is returned in this field. + For \ref MXL862XX_EXTENDEDVLAN_FREE, this field should be valid ID returned by + \ref MXL862XX_EXTENDEDVLAN_ALLOC. */ + u16 extended_vlan_block_id; +} mxl862xx_extendedvlan_alloc_t; + +/** \brief Extended VLAN Treatment. + Used by \ref MXL862XX_EXTENDEDVLAN_config_t. */ +typedef struct { + /** Number of VLAN tag(s) to remove. */ + mxl862xx_extended_vlan_treatment_remove_tag_t remove_tag; + + mxl862xx_extended_vlan_4_tpid_mode_t treatment_4_tpid_mode; + + /** Enable outer VLAN tag add/modification. */ + bool add_outer_vlan; + /** If b_add_outer_vlan is TRUE, add or modify outer VLAN tag. */ + mxl862xx_extendedvlan_treatment_vlan_t outer_vlan; + + /** Enable inner VLAN tag add/modification. */ + bool add_inner_vlan; + /** If b_add_inner_vlan is TRUE, add or modify inner VLAN tag. */ + mxl862xx_extendedvlan_treatment_vlan_t inner_vlan; + + /** Enable re-assignment of bridge port. */ + bool reassign_bridge_port; + /** If b_reassign_bridge_port is TRUE, use this value for bridge port. */ + u16 new_bridge_port_id; + + /** Enable new DSCP. */ + bool new_dscp_enable; + /** If b_new_dscp_enable is TRUE, use this value for DSCP. */ + u16 new_dscp; + + /** Enable new traffic class. */ + bool new_traffic_class_enable; + /** If b_new_traffic_class_enable is TRUE, use this value for traffic class. */ + u8 new_traffic_class; + + /** Enable new meter. */ + bool new_meter_enable; + /** New meter ID. + + \remarks + Meter should be allocated with \ref MXL862XX_QOS_METER_ALLOC before extended + VLAN treatment is added. If this extended VLAN treatment is deleted, + this meter should be released with \ref MXL862XX_QOS_METER_FREE. */ + u16 s_new_traffic_meter_id; + + /** DSCP to PCP mapping, if + \ref MXL862XX_EXTENDEDVLAN_treatment_vlan_t::e_priority_mode in + \ref MXL862XX_EXTENDEDVLAN_treatment_t::s_outer_vlan.e_priority_mode or + \ref MXL862XX_EXTENDEDVLAN_treatment_t::s_inner_vlan.e_priority_mode is + \ref MXL862XX_EXTENDEDVLAN_TREATMENT_DSCP. + + \remarks + The index of array stands for DSCP value. Each byte of the array is 3-bit + PCP value. For implementation, if DSCP2PCP is separate hardware table, + a resource management mechanism should be implemented. Allocation happens + when extended VLAN treatment added, and release happens when the + treatment is deleted. For debug, the DSCP2PCP table can be dumped with + \ref MXL862XX_DSCP2PCP_MAP_GET. */ + u8 dscp2pcp_map[64]; + + /** Enable loopback. */ + bool loopback_enable; + /** Enable destination/source MAC address swap. */ + bool da_sa_swap_enable; + /** Enable traffic mirrored to the monitoring port. */ + bool mirror_enable; +} mxl862xx_extendedvlan_treatment_t; + +/** \brief Extended VLAN Configuration. + Used by \ref MXL862XX_EXTENDEDVLAN_SET and \ref MXL862XX_EXTENDEDVLAN_GET. */ +typedef struct { + /** This should be valid ID returned by \ref MXL862XX_EXTENDEDVLAN_ALLOC. + If it is \ref INVALID_HANDLE, \ref MXL862XX_EXTENDEDVLAN_config_t::n_entry_index + is absolute index of Extended VLAN entry in hardware for debug purpose, + bypassing any check. */ + u16 extended_vlan_block_id; + + /** Index of entry, ranges between 0 and + \ref MXL862XX_EXTENDEDVLAN_alloc_t::n_number_of_entries - 1, to + set (\ref MXL862XX_EXTENDEDVLAN_SET) or get (\ref MXL862XX_EXTENDEDVLAN_GET) + Extended VLAN Configuration entry. For debug purpose, this field could be + absolute index of Entended VLAN entry in hardware, when + \ref MXL862XX_EXTENDEDVLAN_config_t::n_extended_vlan_block_id is + \ref INVALID_HANDLE. */ + u16 entry_index; + + /** Extended VLAN Filter */ + mxl862xx_extendedvlan_filter_t filter; + /** Extended VLAN Treatment */ + mxl862xx_extendedvlan_treatment_t treatment; +} mxl862xx_extendedvlan_config_t; + +/** \brief VLAN Filter Allocation. + Used by \ref MXL862XX_VLANFILTER_ALLOC and \ref MXL862XX_VLANFILTER_FREE. */ +typedef struct { + /** Total number of VLAN Filter entries are requested. Proper value should + be given for \ref MXL862XX_VLANFILTER_ALLOC. This field is ignored for + \ref MXL862XX_VLANFILTER_FREE. */ + u16 number_of_entries; + + /** If \ref MXL862XX_VLANFILTER_ALLOC is successful, a valid ID will be returned + in this field. Otherwise, \ref INVALID_HANDLE is returned in this field. + For \ref MXL862XX_EXTENDEDVLAN_FREE, this field should be valid ID returned by + \ref MXL862XX_VLANFILTER_ALLOC. */ + u16 vlan_filter_block_id; + + /** Discard packet without VLAN tag. */ + bool discard_untagged; + /** Discard VLAN tagged packet not matching any filter entry. */ + bool discard_unmatched_tagged; + /** Use default port VLAN ID for VLAN filtering + + \remarks + This field is not available in PRX300. */ + bool use_default_port_vid; +} mxl862xx_vlanfilter_alloc_t; + +/** \brief VLAN Filter. + Used by \ref MXL862XX_VLANFILTER_SET and \ref MXL862XX_VLANFILTER_GET */ +typedef struct { + /** This should be valid ID return by \ref MXL862XX_VLANFILTER_ALLOC. + If it is \ref INVALID_HANDLE, \ref MXL862XX_VLANFILTER_config_t::n_entry_index + is absolute index of VLAN Filter entry in hardware for debug purpose, + bypassing any check. */ + u16 vlan_filter_block_id; + + /** Index of entry. ranges between 0 and + \ref MXL862XX_VLANFILTER_alloc_t::n_number_of_entries - 1, to + set (\ref MXL862XX_VLANFILTER_SET) or get (\ref MXL862XX_VLANFILTER_GET) + VLAN FIlter entry. For debug purpose, this field could be absolute index + of VLAN Filter entry in hardware, when + \ref MXL862XX_VLANFILTER_config_t::n_vlan_filter_block_id is + \ref INVALID_HANDLE. */ + u16 entry_index; + + /** VLAN TCI filter mask mode. + + \remarks + In GSWIP-3.1, this field of first entry in the block will applies to rest + of entries in the same block. */ + mxl862xx_vlan_filter_tci_mask_t vlan_filter_mask; + + /** This is value for VLAN filtering. It depends on + \ref MXL862XX_VLANFILTER_config_t::e_vlan_filter_mask. + For MXL862XX_VLAN_FILTER_TCI_MASK_VID, this is 12-bit VLAN ID. + For MXL862XX_VLAN_FILTER_TCI_MASK_PCP, this is 3-bit PCP field of VLAN tag. + For MXL862XX_VLAN_FILTER_TCI_MASK_TCI, this is 16-bit TCI of VLAN tag. */ + u32 val; + /** Discard packet if match. */ + bool discard_matched; +} mxl862xx_vlanfilter_config_t; + +/* VLAN Rmon Counters */ +typedef enum { + MXL862XX_VLAN_RMON_RX = 0, + MXL862XX_VLAN_RMON_TX = 1, + MXL862XX_VLAN_RMON__PCE_BYPASS = 2, +} mxl862xx_vlan_rmon_type_t; + +/** + \brief RMON Counters structure for VLAN. */ +typedef struct { + u16 vlan_counter_index; + mxl862xx_vlan_rmon_type_t vlan_rmon_type; + u32 byte_count_high; + u32 byte_count_low; + u32 total_pkt_count; + u32 multicast_pkt_count; + u32 drop_pkt_count; + u32 clear_all; +} mxl862xx_vlan_rmon_cnt_t; + +/** + \brief RMON Counters control structure for VLAN. */ +typedef struct { + bool vlan_rmon_enable; + bool include_broad_cast_pkt_counting; + u32 vlan_last_entry; +} mxl862xx_vlan_rmon_control_t; + +/** \brief VLAN Counter Mapping. */ +typedef enum { + /** VLAN Mapping for Ingress */ + MXL862XX_VLAN_MAPPING_INGRESS = 0, + /** VLAN Mapping for Egress */ + MXL862XX_VLAN_MAPPING_EGRESS = 1, + /** VLAN Mapping for Ingress and Egress */ + MXL862XX_VLAN_MAPPING_INGRESS_AND_EGRESS = 2 +} mxl862xx_vlan_counter_mapping_type_t; + +/** \brief VLAN Counter Mapping Filter. */ +typedef enum { + /** There is tag and criteria applies. */ + MXL862XX_VLANCOUNTERMAP_FILTER_TYPE_NORMAL = 0, + /** There is tag but no criteria. */ + MXL862XX_VLANCOUNTERMAP_FILTER_TYPE_NO_FILTER = 1, + /** Default entry if no other rule applies. */ + MXL862XX_VLANCOUNTERMAP_FILTER_TYPE_DEFAULT = 2, + /** There is no tag. */ + MXL862XX_VLANCOUNTERMAP_FILTER_TYPE_NO_TAG = 3, + /** Filter invalid*/ + MXL862XX_VLANCOUNTERMAP_FILTER_INVALID = 4, +} mxl862xx_vlan_counter_map_filter_type_t; + +/** \brief VLAN Counter Mapping Configuration. */ +typedef struct { + /** Counter Index */ + u8 counter_index; + /** Ctp Port Id */ + u16 ctp_port_id; + /** Priority Enable */ + bool priority_enable; + /** Priority Val */ + u32 priority_val; + /** VLAN Id Enable */ + bool vid_enable; + /** VLAN Id Value */ + u32 vid_val; + /** VLAN Tag Selection Value */ + bool vlan_tag_selection_enable; + /** VLAN Counter Mapping Type */ + mxl862xx_vlan_counter_mapping_type_t vlan_counter_mapping_type; + /** VLAN Counter Mapping Filter Type */ + mxl862xx_vlan_counter_map_filter_type_t vlan_counter_mapping_filter_type; +} mxl862xx_vlan_counter_mapping_config_t; + +/** \brief MAC Table Entry to be added. + Used by \ref MXL862XX_MAC_TABLE_ENTRY_ADD. */ +typedef struct { + /** Filtering Identifier (FID) (not supported by all switches) */ + u16 fid; + /** Ethernet Port number (zero-based counting) in GSWIP-2.1/2.2/3.0. From + GSWIP-3.1, this field is Bridge Port ID. The valid range is hardware + dependent. + + \remarks + In GSWIP-2.1/2.2/3.0, this field is used as portmap field, when the MSB + bit is set. In portmap mode, every value bit represents an Ethernet port. + LSB represents Port 0 with incrementing counting. + The (MSB - 1) bit represent the last port. + The macro \ref MXL862XX_PORTMAP_FLAG_SET allows to set the MSB bit, + marking it as portmap variable. + Checking the portmap flag can be done by + using the \ref MXL862XX_PORTMAP_FLAG_GET macro. + From GSWIP3.1, if MSB is set, other bits in this field are ignored. + array \ref MXL862XX_MAC_table_read_t::n_port_map is used for bit map. */ + u32 port_id; + /** Bridge Port Map - to support GSWIP-3.1, following field is added + for port map in static entry. It's valid only when MSB of + \ref MXL862XX_MAC_table_read_t::n_port_id is set. Each bit stands for 1 bridge + port. */ + u16 port_map[8]; /* max can be 16 */ + /** Sub-Interface Identifier Destination (supported in GSWIP-3.0/3.1 only). + + \remarks + In GSWIP-3.1, this field is sub interface ID for WLAN logical port. For + Other types, either outer VLAN ID if Nto1Vlan enabled or 0. */ + u16 sub_if_id; + /** Aging Time, given in multiples of 1 second in a range + from 1 s to 1,000,000 s. + The configured value might be rounded that it fits to the given hardware platform. */ + int age_timer; + /** STAG VLAN Id. Only applicable in case SVLAN support is enabled on the device. */ + u16 vlan_id; + /** Static Entry (value will be aged out if the entry is not set to static). The + switch API implementation uses the maximum age timer in case the entry + is not static. */ + bool static_entry; + /** Egress queue traffic class. + The queue index starts counting from zero. */ + u8 traffic_class; + /** MAC Address to add to the table. */ + u8 mac[ETH_ALEN]; + /** Source/Destination MAC address filtering flag (GSWIP-3.1 only) + Value 0 - not filter, 1 - source address filter, + 2 - destination address filter, 3 - both source and destination filter. + + \remarks + Please refer to "GSWIP Hardware Architecture Spec" chapter 3.4.4.6 + "Source MAC Address Filtering and Destination MAC Address Filtering" + for more detail. */ + u8 filter_flag; + /** Packet is marked as IGMP controlled if destination MAC address matches + MAC in this entry. (GSWIP-3.1 only) */ + bool igmp_controlled; + + /** Associated Mac address -(GSWIP-3.2)*/ + u8 associated_mac[ETH_ALEN]; + + /** TCI for (GSWIP-3.2) B-Step + Bit [0:11] - VLAN ID + Bit [12] - VLAN CFI/DEI + Bit [13:15] - VLAN PRI */ + u16 tci; +} mxl862xx_mac_table_add_t; + +/** \brief MAC Table Entry to be read. + Used by \ref MXL862XX_MAC_TABLE_ENTRY_READ. */ +typedef struct { + /** Restart the get operation from the beginning of the table. Otherwise + return the next table entry (next to the entry that was returned + during the previous get operation). This boolean parameter is set by the + calling application. */ + bool initial; + /** Indicates that the read operation got all last valid entries of the + table. This boolean parameter is set by the switch API + when the Switch API is called after the last valid one was returned already. */ + bool last; + /** Get the MAC table entry belonging to the given Filtering Identifier + (not supported by all switches). */ + u16 fid; + /** Ethernet Port number (zero-based counting) in GSWIP-2.1/2.2/3.0. From + GSWIP-3.1, this field is Bridge Port ID. The valid range is hardware + dependent. + + \remarks + In GSWIP-2.1/2.2/3.0, this field is used as portmap field, when the MSB + bit is set. In portmap mode, every value bit represents an Ethernet port. + LSB represents Port 0 with incrementing counting. + The (MSB - 1) bit represent the last port. + The macro \ref MXL862XX_PORTMAP_FLAG_SET allows to set the MSB bit, + marking it as portmap variable. + Checking the portmap flag can be done by + using the \ref MXL862XX_PORTMAP_FLAG_GET macro. + From GSWIP3.1, if MSB is set, other bits in this field are ignored. + array \ref MXL862XX_MAC_table_read_t::n_port_map is used for bit map. */ + u32 port_id; + /** Bridge Port Map - to support GSWIP-3.1, following field is added + for port map in static entry. It's valid only when MSB of + \ref MXL862XX_MAC_table_read_t::n_port_id is set. Each bit stands for 1 bridge + port. */ + u16 port_map[8]; /* max can be 16 */ + /** Aging Time, given in multiples of 1 second in a range from 1 s to 1,000,000 s. + The value read back in a GET command might differ slightly from the value + given in the SET command due to limited hardware timing resolution. + Filled out by the switch API implementation. */ + int age_timer; + /** STAG VLAN Id. Only applicable in case SVLAN support is enabled on the device. */ + u16 vlan_id; + /** Static Entry (value will be aged out after 'n_age_timer' if the entry + is not set to static). */ + bool static_entry; + /** Sub-Interface Identifier Destination (supported in GSWIP-3.0/3.1 only). */ + u16 sub_if_id; + /** MAC Address. Filled out by the switch API implementation. */ + u8 mac[ETH_ALEN]; + /** Source/Destination MAC address filtering flag (GSWIP-3.1 only) + Value 0 - not filter, 1 - source address filter, + 2 - destination address filter, 3 - both source and destination filter. + + \remarks + Please refer to "GSWIP Hardware Architecture Spec" chapter 3.4.4.6 + "Source MAC Address Filtering and Destination MAC Address Filtering" + for more detail. */ + u8 filter_flag; + /** Packet is marked as IGMP controlled if destination MAC address matches + MAC in this entry. (GSWIP-3.1 only) */ + bool igmp_controlled; + + /** Changed + 0: the entry is not changed + 1: the entry is changed and not accessed yet */ + + bool entry_changed; + + /** Associated Mac address -(GSWIP-3.2)*/ + u8 associated_mac[ETH_ALEN]; + /* MAC Table Hit Status Update (Supported in GSWip-3.1/3.2) */ + bool hit_status; + /** TCI for (GSWIP-3.2) B-Step + Bit [0:11] - VLAN ID + Bit [12] - VLAN CFI/DEI + Bit [13:15] - VLAN PRI */ + u16 tci; + u16 first_bridge_port_id; +} mxl862xx_mac_table_read_t; + +/** \brief MAC Table Entry to be removed. + Used by \ref MXL862XX_MAC_TABLE_ENTRY_REMOVE. */ +typedef struct { + /** Filtering Identifier (FID) (not supported by all switches) */ + u16 fid; + /** MAC Address to be removed from the table. */ + u8 mac[ETH_ALEN]; + /** Source/Destination MAC address filtering flag (GSWIP-3.1 only) + Value 0 - not filter, 1 - source address filter, + 2 - destination address filter, 3 - both source and destination filter. + + \remarks + Please refer to "GSWIP Hardware Architecture Spec" chapter 3.4.4.6 + "Source MAC Address Filtering and Destination MAC Address Filtering" + for more detail. */ + u8 filter_flag; + /** TCI for (GSWIP-3.2) B-Step + Bit [0:11] - VLAN ID + Bit [12] - VLAN CFI/DEI + Bit [13:15] - VLAN PRI */ + u16 tci; +} mxl862xx_mac_table_remove_t; + +/** \brief Bridge configuration mask. + Used by \ref MXL862XX_BRIDGE_config_t. */ +typedef enum { + /** Mask for \ref MXL862XX_BRIDGE_config_t::b_mac_learning_limit_enable + and \ref MXL862XX_BRIDGE_config_t::n_mac_learning_limit. */ + MXL862XX_BRIDGE_CONFIG_MASK_MAC_LEARNING_LIMIT = 0x00000001, + /** Mask for \ref MXL862XX_BRIDGE_config_t::n_mac_learning_count */ + MXL862XX_BRIDGE_CONFIG_MASK_MAC_LEARNED_COUNT = 0x00000002, + /** Mask for \ref MXL862XX_BRIDGE_config_t::n_learning_discard_event */ + MXL862XX_BRIDGE_CONFIG_MASK_MAC_DISCARD_COUNT = 0x00000004, + /** Mask for \ref MXL862XX_BRIDGE_config_t::b_sub_metering_enable and + \ref MXL862XX_BRIDGE_config_t::n_traffic_sub_meter_id */ + MXL862XX_BRIDGE_CONFIG_MASK_SUB_METER = 0x00000008, + /** Mask for \ref MXL862XX_BRIDGE_config_t::e_forward_broadcast, + \ref MXL862XX_BRIDGE_config_t::e_forward_unknown_multicast_ip, + \ref MXL862XX_BRIDGE_config_t::e_forward_unknown_multicast_non_ip, + and \ref MXL862XX_BRIDGE_config_t::e_forward_unknown_unicast. */ + MXL862XX_BRIDGE_CONFIG_MASK_FORWARDING_MODE = 0x00000010, + + /** Enable all */ + MXL862XX_BRIDGE_CONFIG_MASK_ALL = 0x7FFFFFFF, + /** Bypass any check for debug purpose */ + MXL862XX_BRIDGE_CONFIG_MASK_FORCE = 0x80000000 +} mxl862xx_bridge_config_mask_t; + +/** \brief Bridge forwarding type of packet. + Used by \ref MeXL862XX_BRIDGE_port_config_t. */ +typedef enum { + /** Packet is flooded to port members of ingress bridge port */ + MXL862XX_BRIDGE_FORWARD_FLOOD = 0, + /** Packet is dscarded */ + MXL862XX_BRIDGE_FORWARD_DISCARD = 1, + /** Packet is forwarded to logical port 0 CTP port 0 bridge port 0 */ + MXL862XX_BRIDGE_FORWARD_CPU = 2 +} mxl862xx_bridge_forward_mode_t; + +/** \brief Bridge Configuration. + Used by \ref MXL862XX_BRIDGE_CONFIG_SET and \ref MXL862XX_BRIDGE_CONFIG_GET. */ +typedef struct { + /** Bridge ID (FID) allocated by \ref MXL862XX_BRIDGE_ALLOC. + + \remarks + If \ref MXL862XX_BRIDGE_config_t::e_mask has + \ref MXL862XX_Bridge_config_mask_t::MXL862XX_BRIDGE_CONFIG_MASK_FORCE, this field is + absolute index of Bridge (FID) in hardware for debug purpose, bypassing + any check. */ + u16 bridge_id; + + /** Mask for updating/retrieving fields. */ + mxl862xx_bridge_config_mask_t mask; + + /** Enable MAC learning limitation. */ + bool mac_learning_limit_enable; + /** Max number of MAC can be learned in this bridge (all bridge ports). */ + u16 mac_learning_limit; + + /** Get number of MAC address learned from this bridge port. */ + u16 mac_learning_count; + + /** Number of learning discard event due to hardware resource not available. + + \remarks + This is discard event due to either MAC table full or Hash collision. + Discard due to n_mac_learning_count reached is not counted in this field. */ + u32 learning_discard_event; + + /** Traffic metering on type of traffic (such as broadcast, multicast, + unknown unicast, etc) applies. */ + bool sub_metering_enable[MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX]; + /** Meter for bridge process with specific type (such as broadcast, + multicast, unknown unicast, etc). Need pre-allocated for each type. */ + u16 traffic_sub_meter_id[MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX]; + + /** Forwarding mode of broadcast traffic. */ + mxl862xx_bridge_forward_mode_t forward_broadcast; + /** Forwarding mode of unknown multicast IP traffic. */ + mxl862xx_bridge_forward_mode_t forward_unknown_multicast_ip; + /** Forwarding mode of unknown multicast non-IP traffic. */ + mxl862xx_bridge_forward_mode_t forward_unknown_multicast_non_ip; + /** Forwarding mode of unknown unicast traffic. */ + mxl862xx_bridge_forward_mode_t forward_unknown_unicast; +} mxl862xx_bridge_config_t; + +/** \brief Aging Timer Value. + Used by \ref mxl862xx_cfg_t. */ +typedef enum { + /** 1 second aging time */ + MXL862XX_AGETIMER_1_SEC = 1, + /** 10 seconds aging time */ + MXL862XX_AGETIMER_10_SEC = 2, + /** 300 seconds aging time */ + MXL862XX_AGETIMER_300_SEC = 3, + /** 1 hour aging time */ + MXL862XX_AGETIMER_1_HOUR = 4, + /** 24 hours aging time */ + MXL862XX_AGETIMER_1_DAY = 5, + /** Custom aging time in seconds */ + MXL862XX_AGETIMER_CUSTOM = 6 +} mxl862xx_age_timer_t; + +/** \brief Global Switch configuration Attributes. + Used by \ref MXL862XX_CFG_SET and \ref MXL862XX_CFG_GET. */ +typedef struct { + /** MAC table aging timer. After this timer expires the MAC table + entry is aged out. */ + mxl862xx_age_timer_t mac_table_age_timer; + /** If eMAC_TableAgeTimer = MXL862XX_AGETIMER_CUSTOM, this variable defines + MAC table aging timer in seconds. */ + u32 age_timer; + /** Maximum Ethernet packet length. */ + u16 max_packet_len; + /** Automatic MAC address table learning limitation consecutive action. + These frame addresses are not learned, but there exists control as to whether + the frame is still forwarded or dropped. + + - False: Drop + - True: Forward + */ + bool learning_limit_action; + /** Accept or discard MAC port locking violation packets. + MAC spoofing detection features identifies ingress packets that carry + a MAC source address which was previously learned on a different + ingress port (learned by MAC bridging table). This also applies to + static added entries. MAC address port locking is configured on + port level by 'bLearningMAC_PortLock'. + + - False: Drop + - True: Forward + */ + bool mac_locking_action; + /** Accept or discard MAC spoofing and port MAC locking violation packets. + MAC spoofing detection features identifies ingress packets that carry + a MAC source address which was previously learned on a different + ingress port (learned by MAC bridging table). This also applies to + static added entries. MAC spoofing detection is enabled on port + level by 'bMAC_SpoofingDetection'. + + - False: Drop + - True: Forward + */ + bool mac_spoofing_action; + /** Pause frame MAC source address mode. If enabled, use the alternative + address specified with 'nMAC'. */ + bool pause_mac_mode_src; + /** Pause frame MAC source address. */ + u8 pause_mac_src[ETH_ALEN]; +} mxl862xx_cfg_t; + +/** \brief Sets the portmap flag of a port_iD variable. + Some Switch API commands allow to use a port index as portmap variable. + This requires that the MSB bit is set to indicate that this variable + contains a portmap, instead of a port index. + In portmap mode, every value bit represents an Ethernet port. + LSB represents Port 0 with incrementing counting. + The (MSB - 1) bit represent the last port. */ +#define MXL862XX_PORTMAP_FLAG_SET(var_type) (1 << (sizeof(((var_type *)0)->port_id) * 8 - 1)) + +/** \brief Checks the portmap flag of a port_iD variable. + Some Switch API commands allow to use a port index as portmap variable. + This requires that the MSB bit is set to indicate that this variable + contains a portmap, instead of a port index. + In portmap mode, every value bit represents an Ethernet port. + LSB represents Port 0 with incrementing counting. + The (MSB - 1) bit represent the last port. */ +#define MXL862XX_PORTMAP_FLAG_GET(var_type) (1 << (sizeof(((var_type *)0)->port_id) * 8 - 1)) + +#pragma scalar_storage_order default +#pragma pack(pop) + +#include "mxl862xx_ctp.h" +#include "mxl862xx_flow.h" + +struct sys_fw_image_version { + uint8_t iv_major; + uint8_t iv_minor; + uint16_t iv_revision; + uint32_t iv_build_num; +}; + +int sys_misc_fw_version(const mxl862xx_device_t *dummy, + struct sys_fw_image_version *sys_img_ver); + +int mxl862xx_register_mod(const mxl862xx_device_t *, mxl862xx_register_mod_t *); +int mxl862xx_port_link_cfg_set(const mxl862xx_device_t *, mxl862xx_port_link_cfg_t *); +int mxl862xx_port_link_cfg_get(const mxl862xx_device_t *, mxl862xx_port_link_cfg_t *); +int mxl862xx_port_cfg_set(const mxl862xx_device_t *, mxl862xx_port_cfg_t *); +int mxl862xx_port_cfg_get(const mxl862xx_device_t *, mxl862xx_port_cfg_t *); +/* Bridge */ +int mxl862xx_bridge_alloc(const mxl862xx_device_t *, mxl862xx_bridge_alloc_t *); +int mxl862xx_bridge_free(const mxl862xx_device_t *, mxl862xx_bridge_alloc_t *); +int mxl862xx_bridge_config_set(const mxl862xx_device_t *dev, mxl862xx_bridge_config_t *); +int mxl862xx_bridge_config_get(const mxl862xx_device_t *dev, mxl862xx_bridge_config_t *); +/* Bridge Port */ +int mxl862xx_bridge_port_alloc(const mxl862xx_device_t *, + mxl862xx_bridge_port_alloc_t *); +int mxl862xx_bridge_port_free(const mxl862xx_device_t *, + mxl862xx_bridge_port_alloc_t *); +int mxl862xx_bridge_port_config_set(const mxl862xx_device_t *, + mxl862xx_bridge_port_config_t *); +int mxl862xx_bridge_port_config_get(const mxl862xx_device_t *, + mxl862xx_bridge_port_config_t *); +/* Debug */ +int mxl862xx_debug_rmon_port_get(const mxl862xx_device_t *, + mxl862xx_debug_rmon_port_cnt_t *); + +int mxl862xx_mac_table_clear(const mxl862xx_device_t *); +int mxl862xx_mac_table_clear_cond(const mxl862xx_device_t *, + mxl862xx_mac_table_clear_cond_t *); +int mxl862xx_mac_table_entry_read(const mxl862xx_device_t *, mxl862xx_mac_table_read_t *); +int mxl862xx_mac_table_entry_add(const mxl862xx_device_t *, mxl862xx_mac_table_add_t *); +int mxl862xx_mac_table_entry_remove(const mxl862xx_device_t *, + mxl862xx_mac_table_remove_t *); +int mxl862xx_stp_port_cfg_set(const mxl862xx_device_t *, mxl862xx_stp_port_cfg_t *parm); + +int mxl862xx_ss_sp_tag_get(const mxl862xx_device_t *, mxl862xx_ss_sp_tag_t *); +int mxl862xx_ss_sp_tag_set(const mxl862xx_device_t *, mxl862xx_ss_sp_tag_t *); + +int mxl862xx_ctp_port_config_get(const mxl862xx_device_t *, mxl862xx_ctp_port_config_t *); +int mxl862xx_ctp_port_config_set(const mxl862xx_device_t *, mxl862xx_ctp_port_config_t *); +int mxl862xx_ctp_port_assignment_set(const mxl862xx_device_t *, + mxl862xx_ctp_port_assignment_t *); +int mxl862xx_ctp_port_assignment_get(const mxl862xx_device_t *, + mxl862xx_ctp_port_assignment_t *); +int mxl862xx_monitor_port_cfg_get(const mxl862xx_device_t *, mxl862xx_monitor_port_cfg_t *); +int mxl862xx_monitor_port_cfg_set(const mxl862xx_device_t *, mxl862xx_monitor_port_cfg_t *); + +int mxl862xx_extended_vlan_alloc(const mxl862xx_device_t *, + mxl862xx_extendedvlan_alloc_t *); +int mxl862xx_extended_vlan_set(const mxl862xx_device_t *, + mxl862xx_extendedvlan_config_t *); +int mxl862xx_extended_vlan_get(const mxl862xx_device_t *, + mxl862xx_extendedvlan_config_t *); +int mxl862xx_extended_vlan_free(const mxl862xx_device_t *, + mxl862xx_extendedvlan_alloc_t *); +int mxl862xx_vlan_filter_alloc(const mxl862xx_device_t *, mxl862xx_vlanfilter_alloc_t *); +int mxl862xx_vlan_filter_set(const mxl862xx_device_t *, mxl862xx_vlanfilter_config_t *); +int mxl862xx_vlan_filter_get(const mxl862xx_device_t *, mxl862xx_vlanfilter_config_t *); +int mxl862xx_vlan_filter_free(const mxl862xx_device_t *, mxl862xx_vlanfilter_alloc_t *); +int mxl862xx_cfg_get(const mxl862xx_device_t *, mxl862xx_cfg_t *); +int mxl862xx_cfg_set(const mxl862xx_device_t *, mxl862xx_cfg_t *); +#endif /* _MXL862XX_API_H_ */ diff --git a/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_ctp.h b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_ctp.h new file mode 100644 index 000000000000..683fa8f27c2b --- /dev/null +++ b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_ctp.h @@ -0,0 +1,336 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * drivers/net/dsa/host_api/mxl862xx_ctp.h - Header file for DSA Driver for MaxLinear Mxl862xx switch chips family + * + * Copyright (C) 2024 MaxLinear Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ +#ifndef _MXL862XX_CTP_H_ +#define _MXL862XX_CTP_H_ + +#include "mxl862xx_types.h" + +#pragma pack(push, 1) +#pragma scalar_storage_order little-endian + +/** \brief Logical port mode. + Used by \ref MXL862XX_CTP_port_assignment_t. */ +typedef enum { + /** WLAN with 8-bit station ID */ + MXL862XX_LOGICAL_PORT_8BIT_WLAN = 0, + /** WLAN with 9-bit station ID */ + MXL862XX_LOGICAL_PORT_9BIT_WLAN = 1, + /** GPON OMCI context */ + MXL862XX_LOGICAL_PORT_GPON = 2, + /** EPON context */ + MXL862XX_LOGICAL_PORT_EPON = 3, + /** G.INT context */ + MXL862XX_LOGICAL_PORT_GINT = 4, + /** Others (sub interface ID is 0 by default) */ + MXL862XX_LOGICAL_PORT_OTHER = 0xFF, +} mxl862xx_logical_port_mode_t; + +/** \brief CTP Port configuration mask. + Used by \ref MXL862XX_CTP_port_config_t. */ +typedef enum { + /** Mask for \ref MXL862XX_CTP_port_config_t::bridge_port_id */ + MXL862XX_CTP_PORT_CONFIG_MASK_BRIDGE_PORT_ID = 0x00000001, + /** Mask for \ref MXL862XX_CTP_port_config_t::b_forced_traffic_class and + \ref MXL862XX_CTP_port_config_t::n_default_traffic_class */ + MXL862XX_CTP_PORT_CONFIG_MASK_FORCE_TRAFFIC_CLASS = 0x00000002, + /** Mask for \ref MXL862XX_CTP_port_config_t::b_ingress_extended_vlan_enable and + \ref MXL862XX_CTP_port_config_t::n_ingress_extended_vlan_block_id */ + MXL862XX_CTP_PORT_CONFIG_MASK_INGRESS_VLAN = 0x00000004, + /** Mask for \ref MXL862XX_CTP_port_config_t::b_ingress_extended_vlan_igmp_enable and + \ref MXL862XX_CTP_port_config_t::n_ingress_extended_vlan_block_id_igmp */ + MXL862XX_CTP_PORT_CONFIG_MASK_INGRESS_VLAN_IGMP = 0x00000008, + /** Mask for \ref MXL862XX_CTP_port_config_t::b_egress_extended_vlan_enable and + \ref MXL862XX_CTP_port_config_t::n_egress_extended_vlan_block_id */ + MXL862XX_CTP_PORT_CONFIG_MASK_EGRESS_VLAN = 0x00000010, + /** Mask for \ref MXL862XX_CTP_port_config_t::b_egress_extended_vlan_igmp_enable and + \ref MXL862XX_CTP_port_config_t::n_egress_extended_vlan_block_id_igmp */ + MXL862XX_CTP_PORT_CONFIG_MASK_EGRESS_VLAN_IGMP = 0x00000020, + /** Mask for \ref MXL862XX_CTP_port_config_t::b_ingress_nto1Vlan_enable */ + MXL862XX_CTP_PORT_CONFIG_MASK_INRESS_NTO1_VLAN = 0x00000040, + /** Mask for \ref MXL862XX_CTP_port_config_t::b_egress_nto1Vlan_enable */ + MXL862XX_CTP_PORT_CONFIG_MASK_EGRESS_NTO1_VLAN = 0x00000080, + /** Mask for \ref MXL862XX_CTP_port_config_t::e_ingress_marking_mode */ + MXL862XX_CTP_PORT_CONFIG_INGRESS_MARKING = 0x00000100, + /** Mask for \ref MXL862XX_CTP_port_config_t::e_egress_marking_mode */ + MXL862XX_CTP_PORT_CONFIG_EGRESS_MARKING = 0x00000200, + /** Mask for \ref MXL862XX_CTP_port_config_t::b_egress_marking_override_enable and + \ref MXL862XX_CTP_port_config_t::e_egress_marking_mode_override */ + MXL862XX_CTP_PORT_CONFIG_EGRESS_MARKING_OVERRIDE = 0x00000400, + /** Mask for \ref MXL862XX_CTP_port_config_t::e_egress_remarking_mode */ + MXL862XX_CTP_PORT_CONFIG_EGRESS_REMARKING = 0x00000800, + /** Mask for \ref MXL862XX_CTP_port_config_t::b_ingress_metering_enable and + \ref MXL862XX_CTP_port_config_t::n_ingress_traffic_meter_id */ + MXL862XX_CTP_PORT_CONFIG_INGRESS_METER = 0x00001000, + /** Mask for \ref MXL862XX_CTP_port_config_t::b_egress_metering_enable and + \ref MXL862XX_CTP_port_config_t::n_egress_traffic_meter_id */ + MXL862XX_CTP_PORT_CONFIG_EGRESS_METER = 0x00002000, + /** Mask for \ref MXL862XX_CTP_port_config_t::b_bridging_bypass, + \ref MXL862XX_CTP_port_config_t::n_dest_logical_port_id, + \ref MXL862XX_CTP_port_config_t::b_pmapper_enable, + \ref MXL862XX_CTP_port_config_t::n_dest_sub_if_id_group, + \ref MXL862XX_CTP_port_config_t::e_pmapper_mapping_mode + \ref mxl862xx_bridge_port_config_t::b_pmapper_id_valid and + \ref MXL862XX_CTP_port_config_t::s_pmapper */ + MXL862XX_CTP_PORT_CONFIG_BRIDGING_BYPASS = 0x00004000, + /** Mask for \ref MXL862XX_CTP_port_config_t::n_first_flow_entry_index and + \ref MXL862XX_CTP_port_config_t::n_number_of_flow_entries */ + MXL862XX_CTP_PORT_CONFIG_FLOW_ENTRY = 0x00008000, + /** Mask for \ref MXL862XX_CTP_port_config_t::b_ingress_loopback_enable, + \ref MXL862XX_CTP_port_config_t::b_ingress_da_sa_swap_enable, + \ref MXL862XX_CTP_port_config_t::b_egress_loopback_enable, + \ref MXL862XX_CTP_port_config_t::b_egress_da_sa_swap_enable, + \ref MXL862XX_CTP_port_config_t::b_ingress_mirror_enable and + \ref MXL862XX_CTP_port_config_t::b_egress_mirror_enable */ + MXL862XX_CTP_PORT_CONFIG_LOOPBACK_AND_MIRROR = 0x00010000, + + /** Enable all fields */ + MXL862XX_CTP_PORT_CONFIG_MASK_ALL = 0x7FFFFFFF, + /** Bypass any check for debug purpose */ + MXL862XX_CTP_PORT_CONFIG_MASK_FORCE = 0x80000000 +} mxl862xx_ctp_port_config_mask_t; + +/** \brief CTP Port Assignment/association with logical port. + Used by \ref MXL862XX_CTP_PORT_ASSIGNMENT_ALLOC, \ref MXL862XX_CTP_PORT_ASSIGNMENT_SET + and \ref MXL862XX_CTP_PORT_ASSIGNMENT_GET. */ +typedef struct { + /** Logical Port Id. The valid range is hardware dependent. */ + u8 logical_port_id; + + /** First CTP Port ID mapped to above logical port ID. + + \remarks + For \ref MXL862XX_CTP_PORT_ASSIGNMENT_ALLOC, this is output when CTP + allocation is successful. For other APIs, this is input. */ + u16 first_ctp_port_id; + /** Total number of CTP Ports mapped above logical port ID. */ + u16 number_of_ctp_port; + + /** Logical port mode to define sub interface ID format. */ + mxl862xx_logical_port_mode_t mode; + + /** Bridge ID (FID) + + \remarks + For \ref MXL862XX_CTP_PORT_ASSIGNMENT_ALLOC, this is input. Each CTP allocated + is mapped to Bridge Port given by this field. The Bridge Port will be + configured to use first CTP + (\ref MXL862XX_CTP_port_assignment_t::n_first_ctp_port_id) as egress CTP. + For other APIs, this is ignored. */ + u16 bridge_port_id; +} mxl862xx_ctp_port_assignment_t; + +/** \brief CTP Port Configuration. + Used by \ref MXL862XX_CTP_PORT_CONFIG_SET and \ref MXL862XX_CTP_PORT_CONFIG_GET. */ +typedef struct { + /** Logical Port Id. The valid range is hardware dependent. + If \ref MXL862XX_CTP_port_config_t::e_mask has + \ref MXL862XX_Ctp_port_config_mask_t::MXL862XX_CTP_PORT_CONFIG_MASK_FORCE, this field + is ignored. */ + u8 logical_port_id; + + /** Sub interface ID group. The valid range is hardware/protocol dependent. + + \remarks + Sub interface ID group is defined for each of \ref MXL862XX_Logical_port_mode_t. + For both \ref MXL862XX_LOGICAL_PORT_8BIT_WLAN and + \ref MXL862XX_LOGICAL_PORT_9BIT_WLAN, this field is VAP. + For \ref MXL862XX_LOGICAL_PORT_GPON, this field is GEM index. + For \ref MXL862XX_LOGICAL_PORT_EPON, this field is stream index. + For \ref MXL862XX_LOGICAL_PORT_GINT, this field is LLID. + For others, this field is 0. + If \ref MXL862XX_CTP_port_config_t::e_mask has + \ref MXL862XX_Ctp_port_config_mask_t::MXL862XX_CTP_PORT_CONFIG_MASK_FORCE, this field + is absolute index of CTP in hardware for debug purpose, bypassing + any check. */ + u16 n_sub_if_id_group; + + /** Mask for updating/retrieving fields. */ + mxl862xx_ctp_port_config_mask_t mask; + + /** Ingress Bridge Port ID to which this CTP port is associated for ingress + traffic. */ + u16 bridge_port_id; + + /** Default traffic class can not be overridden by other rules (except + traffic flow table and special tag) in processing stages. */ + bool forced_traffic_class; + /** Default traffic class associated with all ingress traffic from this CTP + Port. */ + u8 default_traffic_class; + + /** Enable Extended VLAN processing for ingress non-IGMP traffic. */ + bool ingress_extended_vlan_enable; + /** Extended VLAN block allocated for ingress non-IGMP traffic. It defines + extended VLAN process for ingress non-IGMP traffic. Valid when + b_ingress_extended_vlan_enable is TRUE. */ + u16 ingress_extended_vlan_block_id; + /** Extended VLAN block size for ingress non-IGMP traffic. This is optional. + If it is 0, the block size of n_ingress_extended_vlan_block_id will be used. + Otherwise, this field will be used. */ + u16 ingress_extended_vlan_block_size; + /** Enable extended VLAN processing for ingress IGMP traffic. */ + bool ingress_extended_vlan_igmp_enable; + /** Extended VLAN block allocated for ingress IGMP traffic. It defines + extended VLAN process for ingress IGMP traffic. Valid when + b_ingress_extended_vlan_igmp_enable is TRUE. */ + u16 ingress_extended_vlan_block_id_igmp; + /** Extended VLAN block size for ingress IGMP traffic. This is optional. + If it is 0, the block size of n_ingress_extended_vlan_block_id_igmp will be + used. Otherwise, this field will be used. */ + u16 ingress_extended_vlan_block_size_igmp; + + /** Enable extended VLAN processing for egress non-IGMP traffic. */ + bool egress_extended_vlan_enable; + /** Extended VLAN block allocated for egress non-IGMP traffic. It defines + extended VLAN process for egress non-IGMP traffic. Valid when + b_egress_extended_vlan_enable is TRUE. */ + u16 egress_extended_vlan_block_id; + /** Extended VLAN block size for egress non-IGMP traffic. This is optional. + If it is 0, the block size of n_egress_extended_vlan_block_id will be used. + Otherwise, this field will be used. */ + u16 egress_extended_vlan_block_size; + /** Enable extended VLAN processing for egress IGMP traffic. */ + bool egress_extended_vlan_igmp_enable; + /** Extended VLAN block allocated for egress IGMP traffic. It defines + extended VLAN process for egress IGMP traffic. Valid when + b_egress_extended_vlan_igmp_enable is TRUE. */ + u16 egress_extended_vlan_block_id_igmp; + /** Extended VLAN block size for egress IGMP traffic. This is optional. + If it is 0, the block size of n_egress_extended_vlan_block_id_igmp will be + used. Otherwise, this field will be used. */ + u16 egress_extended_vlan_block_size_igmp; + + /** For WLAN type logical port, this should be FALSE. For other types, if + enabled and ingress packet is VLAN tagged, outer VLAN ID is used for + "n_sub_if_id" field in MAC table, otherwise, 0 is used for "n_sub_if_id". */ + bool ingress_nto1vlan_enable; + /** For WLAN type logical port, this should be FALSE. For other types, if + enabled and egress packet is known unicast, outer VLAN ID is from + "n_sub_if_id" field in MAC table. */ + bool egress_nto1vlan_enable; + + /** Ingress color marking mode for ingress traffic. */ + mxl862xx_color_marking_mode_t ingress_marking_mode; + /** Egress color marking mode for ingress traffic at egress priority queue + color marking stage */ + mxl862xx_color_marking_mode_t egress_marking_mode; + /** Egress color marking mode override color marking mode from last stage. */ + bool egress_marking_override_enable; + /** Egress color marking mode for egress traffic. Valid only when + b_egress_marking_override is TRUE. */ + mxl862xx_color_marking_mode_t egress_marking_mode_override; + + /** Color remarking for egress traffic. */ + mxl862xx_color_remarking_mode_t egress_remarking_mode; + + /** Traffic metering on ingress traffic applies. */ + bool ingress_metering_enable; + /** Meter for ingress CTP process. + + \remarks + Meter should be allocated with \ref MXL862XX_QOS_METER_ALLOC before CTP + port configuration. If this CTP port is re-set, the last used meter + should be released. */ + u16 ingress_traffic_meter_id; + /** Traffic metering on egress traffic applies. */ + bool egress_metering_enable; + /** Meter for egress CTP process. + + \remarks + Meter should be allocated with \ref MXL862XX_QOS_METER_ALLOC before CTP + port configuration. If this CTP port is re-set, the last used meter + should be released. */ + u16 egress_traffic_meter_id; + + /** Ingress traffic bypass bridging/multicast processing. Following + parameters are used to determine destination. Traffic flow table is not + bypassed. */ + bool bridging_bypass; + /** When b_bridging_bypass is TRUE, this field defines destination logical + port. */ + u8 dest_logical_port_id; + /** When b_bridging_bypass is TRUE, this field indicates whether to use + \ref MXL862XX_CTP_port_config_t::n_dest_sub_if_id_group or + \ref MXL862XX_CTP_port_config_t::e_pmapper_mapping_mode/ + \ref MXL862XX_CTP_port_config_t::s_pmapper. */ + bool pmapper_enable; + /** When b_bridging_bypass is TRUE and b_pmapper_enable is FALSE, this field + defines destination sub interface ID group. */ + u16 dest_sub_if_id_group; + /** When b_bridging_bypass is TRUE and b_pmapper_enable is TRUE, this field + selects either DSCP or PCP to derive sub interface ID. */ + mxl862xx_pmapper_mapping_mode_t pmapper_mapping_mode; + /** When b_pmapper_enable is TRUE, P-mapper is used. This field determines + whether s_pmapper.n_pmapper_id is valid. If this field is TRUE, the + P-mapper is re-used and no allocation of new P-mapper or value + change in the P-mapper. If this field is FALSE, allocation is + taken care in the API implementation. */ + bool pmapper_id_valid; + /** When b_bridging_bypass is TRUE and b_pmapper_enable is TRUE, P-mapper is + used. If b_pmapper_id_valid is FALSE, API implementation need take care + of P-mapper allocation, and maintain the reference counter of + P-mapper used multiple times. If b_pmapper_id_valid is TRUE, only + s_pmapper.n_pmapper_id is used to associate the P-mapper, and there is + no allocation of new P-mapper or value change in the P-mapper. */ + mxl862xx_pmapper_t pmapper; + + /** First traffic flow table entry is associated to this CTP port. Ingress + traffic from this CTP port will go through traffic flow table search + starting from n_first_flow_entry_index. Should be times of 4. */ + u16 first_flow_entry_index; + /** Number of traffic flow table entries are associated to this CTP port. + Ingress traffic from this CTP port will go through PCE rules search + ending at (n_first_flow_entry_index+n_number_of_flow_entries)-1. Should + be times of 4. */ + u16 number_of_flow_entries; + + /** Ingress traffic from this CTP port will be redirected to ingress + logical port of this CTP port with source sub interface ID used as + destination sub interface ID. Following processing except traffic + flow table search is bypassed if loopback enabled. */ + bool ingress_loopback_enable; + /** Destination/Source MAC address of ingress traffic is swapped before + transmitted (not swapped during PCE processing stages). If destination + is multicast, there is no swap, but source MAC address is replaced + with global configurable value. */ + bool ingress_da_sa_swap_enable; + /** Egress traffic to this CTP port will be redirected to ingress logical + port with same sub interface ID as ingress. */ + bool egress_loopback_enable; + /** Destination/Source MAC address of egress traffic is swapped before + transmitted. */ + bool egress_da_sa_swap_enable; + + /** If enabled, ingress traffic is mirrored to the monitoring port. + \remarks + This should be used exclusive with b_ingress_loopback_enable. */ + bool ingress_mirror_enable; + /** If enabled, egress traffic is mirrored to the monitoring port. + \remarks + This should be used exclusive with b_egress_loopback_enable. */ + bool egress_mirror_enable; +} mxl862xx_ctp_port_config_t; + +#pragma scalar_storage_order default +#pragma pack(pop) + +#endif /*_MXL862XX_CTP_H */ diff --git a/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_flow.h b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_flow.h new file mode 100644 index 000000000000..69fde1470e1f --- /dev/null +++ b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_flow.h @@ -0,0 +1,51 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * drivers/net/dsa/host_api/mxl862xx_flow.h - Header file for DSA Driver for MaxLinear Mxl862xx switch chips family + * + * Copyright (C) 2024 MaxLinear Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef _MXL862XX_FLOW_H_ +#define _MXL862XX_FLOW_H_ + +#include "mxl862xx_types.h" + +#pragma pack(push, 1) +#pragma scalar_storage_order little-endian + +#define MPCE_RULES_INDEX 0 +#define MPCE_RULES_INDEX_LAST (MPCE_RULES_INDEX + 7) +#define EAPOL_PCE_RULE_INDEX 8 +#define BPDU_PCE_RULE_INDEX 9 +#define PFC_PCE_RULE_INDEX 10 + +/** \brief Register access parameter to directly modify internal registers. + Used by \ref GSW_REGISTER_MOD. */ +typedef struct { + /** Register Address Offset for modifiation. */ + u16 reg_addr; + /** Value to write to 'reg_addr'. */ + u16 data; + /** Mask of bits to be modified. 1 to modify, 0 to ignore. */ + u16 mask; +} mxl862xx_register_mod_t; + +#pragma scalar_storage_order default +#pragma pack(pop) + +#endif /* _MXL862XX_FLOW_H_ */ diff --git a/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_host_api_impl.c b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_host_api_impl.c new file mode 100644 index 000000000000..1c91d2ccb28d --- /dev/null +++ b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_host_api_impl.c @@ -0,0 +1,514 @@ +// spdx-license-identifier: gpl-2.0 +/* + * drivers/net/dsa/host_api/mxl862xx_host_api_impl.c - dsa driver for maxlinear mxl862xx switch chips family + * + * copyright (c) 2024 maxlinear inc. + * + * this program is free software; you can redistribute it and/or + * modify it under the terms of the gnu general public license + * as published by the free software foundation; either version 2 + * of the license, or (at your option) any later version. + * + * this program is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. see the + * gnu general public license for more details. + * + * you should have received a copy of the gnu general public license + * along with this program; if not, write to the free software + * foundation, inc., 51 franklin street, fifth floor, boston, ma 02110-1301, usa. + * + */ + +#include "mxl862xx_mmd_apis.h" + +#define CTRL_BUSY_MASK BIT(15) +#define CTRL_CMD_MASK (BIT(15) - 1) + +#define MAX_BUSY_LOOP 1000 /* roughly 10ms */ + +#define THR_RST_DATA 5 + +#define ENABLE_GETSET_OPT 1 + +//#define C22_MDIO + +#if defined(ENABLE_GETSET_OPT) && ENABLE_GETSET_OPT +static struct { + uint16_t ctrl; + int16_t ret; + mmd_api_data_t data; +} shadow = { .ctrl = ~0, .ret = -1, .data = { { 0 } } }; +#endif + +/* required for Clause 22 extended read/write access */ +#define MXL862XX_MMDDATA 0xE +#define MXL862XX_MMDCTRL 0xD + +#define MXL862XX_ACTYPE_ADDRESS (0 << 14) +#define MXL862XX_ACTYPE_DATA (1 << 14) + +#ifndef LINUX_VERSION_CODE +#include +#else +#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c)) +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0)) +#include +#endif + +#ifdef C22_MDIO +/** + * write access to MMD register of PHYs via Clause 22 extended access + */ +static int __mxl862xx_c22_ext_mmd_write(const mxl862xx_device_t *dev, struct mii_bus *bus, int sw_addr, int mmd, + int reg, u16 val) +{ + int res; + + /* Set the DevID for Write Command */ + res = __mdiobus_write(bus, sw_addr, MXL862XX_MMDCTRL, mmd); + if (res < 0) + goto error; + + /* Issue the write command */ + res = __mdiobus_write(bus, sw_addr, MXL862XX_MMDDATA, reg); + if (res < 0) + goto error; + + /* Set the DevID for Write Command */ + res = __mdiobus_write(bus, sw_addr, MXL862XX_MMDCTRL, MXL862XX_ACTYPE_DATA | mmd); + if (res < 0) + goto error; + + /* Issue the write command */ + res = __mdiobus_write(bus, sw_addr, MXL862XX_MMDDATA, val); + if (res < 0) + goto error; + +error: + return res; +} + +/** + * read access to MMD register of PHYs via Clause 22 extended access + */ +static int __mxl862xx_c22_ext_mmd_read(const mxl862xx_device_t *dev, struct mii_bus *bus, int sw_addr, int mmd, int reg) +{ + int res; + +/* Set the DevID for Write Command */ + res = __mdiobus_write(bus, sw_addr, MXL862XX_MMDCTRL, mmd); + if (res < 0) + goto error; + + /* Issue the write command */ + res = __mdiobus_write(bus, sw_addr, MXL862XX_MMDDATA, reg); + if (res < 0) + goto error; + + /* Set the DevID for Write Command */ + res = __mdiobus_write(bus, sw_addr, MXL862XX_MMDCTRL, MXL862XX_ACTYPE_DATA | mmd); + if (res < 0) + goto error; + + /* Read the data */ + res = __mdiobus_read(bus, sw_addr, MXL862XX_MMDDATA); + if (res < 0) + goto error; + +error: + return res; +} +#endif + +int mxl862xx_read(const mxl862xx_device_t *dev, uint32_t regaddr) +{ + int mmd = MXL862XX_MMD_DEV; +#ifdef C22_MDIO + int ret = __mxl862xx_c22_ext_mmd_read(dev, dev->bus, dev->sw_addr, mmd, regaddr); +#else +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 8, 0)) + u32 addr = MII_ADDR_C45 | (mmd << 16) | (regaddr & 0xffff); + int ret = __mdiobus_read(dev->bus, dev->sw_addr, addr); +#else + int ret = __mdiobus_c45_read(dev->bus, dev->sw_addr, mmd, regaddr); +#endif +#endif + return ret; +} + +int mxl862xx_write(const mxl862xx_device_t *dev, uint32_t regaddr, uint16_t data) +{ + int mmd = MXL862XX_MMD_DEV; +#ifdef C22_MDIO + int ret = __mxl862xx_c22_ext_mmd_write(dev, dev->bus, dev->sw_addr, mmd, regaddr, data); +#else +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 8, 0)) + u32 addr = MII_ADDR_C45 | (mmd << 16) | (regaddr & 0xffff); + int ret = __mdiobus_write(dev->bus, dev->sw_addr, addr, data); +#else + int ret = __mdiobus_c45_write(dev->bus, dev->sw_addr, mmd, regaddr, data); +#endif +#endif + return ret; +} + +static int __wait_ctrl_busy(const mxl862xx_device_t *dev) +{ + int ret, i; + + for (i = 0; i < MAX_BUSY_LOOP; i++) { + ret = mxl862xx_read(dev, MXL862XX_MMD_REG_CTRL); + if (ret < 0) { + goto busy_check_exit; + } + + if (!(ret & CTRL_BUSY_MASK)) { + ret = 0; + goto busy_check_exit; + } + + usleep_range(10, 15); + } + ret = -ETIMEDOUT; +busy_check_exit: + return ret; +} + +static int __mxl862xx_rst_data(const mxl862xx_device_t *dev) +{ + int ret; + + ret = mxl862xx_write(dev, MXL862XX_MMD_REG_LEN_RET, 0); + if (ret < 0) + return ret; + + ret = mxl862xx_write(dev, MXL862XX_MMD_REG_CTRL, + MMD_API_RST_DATA | CTRL_BUSY_MASK); + if (ret < 0) + return ret; + + return __wait_ctrl_busy(dev); +} + +static int __mxl862xx_set_data(const mxl862xx_device_t *dev, uint16_t words) +{ + int ret; + uint16_t cmd; + + ret = mxl862xx_write(dev, MXL862XX_MMD_REG_LEN_RET, + MXL862XX_MMD_REG_DATA_MAX_SIZE * sizeof(uint16_t)); + if (ret < 0) + return ret; + + cmd = words / MXL862XX_MMD_REG_DATA_MAX_SIZE - 1; + if (!(cmd < 2)) + return -EINVAL; + + cmd += MMD_API_SET_DATA_0; + ret = mxl862xx_write(dev, MXL862XX_MMD_REG_CTRL, cmd | CTRL_BUSY_MASK); + if (ret < 0) + return ret; + + return __wait_ctrl_busy(dev); +} + +static int __mxl862xx_get_data(const mxl862xx_device_t *dev, uint16_t words) +{ + int ret; + uint16_t cmd; + + ret = mxl862xx_write(dev, MXL862XX_MMD_REG_LEN_RET, + MXL862XX_MMD_REG_DATA_MAX_SIZE * sizeof(uint16_t)); + if (ret < 0) + return ret; + + cmd = words / MXL862XX_MMD_REG_DATA_MAX_SIZE; + if (!(cmd > 0 && cmd < 3)) + return -EINVAL; + cmd += MMD_API_GET_DATA_0; + ret = mxl862xx_write(dev, MXL862XX_MMD_REG_CTRL, cmd | CTRL_BUSY_MASK); + if (ret < 0) + return ret; + + return __wait_ctrl_busy(dev); +} + +static int __mxl862xx_send_cmd(const mxl862xx_device_t *dev, uint16_t cmd, uint16_t size, + int16_t *presult) +{ + int ret; + + ret = mxl862xx_write(dev, MXL862XX_MMD_REG_LEN_RET, size); + if (ret < 0) { + return ret; + } + + ret = mxl862xx_write(dev, MXL862XX_MMD_REG_CTRL, cmd | CTRL_BUSY_MASK); + if (ret < 0) { + return ret; + } + + ret = __wait_ctrl_busy(dev); + if (ret < 0) { + return ret; + } + + ret = mxl862xx_read(dev, MXL862XX_MMD_REG_LEN_RET); + if (ret < 0) { + return ret; + } + + *presult = ret; + return 0; +} + +static bool __mxl862xx_cmd_r_valid(uint16_t cmd_r) +{ +#if defined(ENABLE_GETSET_OPT) && ENABLE_GETSET_OPT + return (shadow.ctrl == cmd_r && shadow.ret >= 0) ? true : false; +#else + return false; +#endif +} + +/* This is usually used to implement CFG_SET command. + * With previous CFG_GET command executed properly, the retrieved data + * are shadowed in local structure. WSP FW has a set of shadow too, + * so that only the difference to be sent over SMDIO. + */ +static int __mxl862xx_api_wrap_cmd_r(const mxl862xx_device_t *dev, uint16_t cmd, + void *pdata, uint16_t size, uint16_t r_size) +{ +#if defined(ENABLE_GETSET_OPT) && ENABLE_GETSET_OPT + int ret; + + uint16_t max, i; + uint16_t *data; + int16_t result = 0; + + max = (size + 1) / 2; + data = pdata; + + ret = __wait_ctrl_busy(dev); + if (ret < 0) { + return ret; + } + + for (i = 0; i < max; i++) { + uint16_t off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE; + + if (i && off == 0) { + /* Send command to set data when every + * MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs are written + * and reload next batch of data from last CFG_GET. + */ + ret = __mxl862xx_set_data(dev, i); + if (ret < 0) { + return ret; + } + } + + if (data[i] == shadow.data.data[i]) + continue; + + mxl862xx_write(dev, MXL862XX_MMD_REG_DATA_FIRST + off, + le16_to_cpu(data[i])); + //sys_le16_to_cpu(data[i])); + } + + ret = __mxl862xx_send_cmd(dev, cmd, size, &result); + if (ret < 0) { + return ret; + } + + if (result < 0) { + return result; + } + + max = (r_size + 1) / 2; + for (i = 0; i < max; i++) { + uint16_t off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE; + + if (i && off == 0) { + /* Send command to fetch next batch of data + * when every MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs + * are read. + */ + ret = __mxl862xx_get_data(dev, i); + if (ret < 0) { + return ret; + } + } + + ret = mxl862xx_read(dev, MXL862XX_MMD_REG_DATA_FIRST + off); + if (ret < 0) { + return ret; + } + + if ((i * 2 + 1) == r_size) { + /* Special handling for last BYTE + * if it's not WORD aligned. + */ + *(uint8_t *)&data[i] = ret & 0xFF; + } else { + data[i] = cpu_to_le16((uint16_t)ret); + } + } + + shadow.data.data[max] = 0; + memcpy(shadow.data.data, data, r_size); + + return result; +#else /* defined(ENABLE_GETSET_OPT) && ENABLE_GETSET_OPT */ + ARG_UNUSED(dev); + ARG_UNUSED(cmd); + ARG_UNUSED(pdata); + ARG_UNUSED(size); + return -ENOTSUP; +#endif /* defined(ENABLE_GETSET_OPT) && ENABLE_GETSET_OPT */ +} + +int mxl862xx_api_wrap(const mxl862xx_device_t *dev, uint16_t cmd, void *pdata, + uint16_t size, uint16_t cmd_r, uint16_t r_size) +{ + int ret; + uint16_t max, i, cnt; + uint16_t *data; + int16_t result = 0; + + if (!dev || (!pdata && size)) + return -EINVAL; + + if (!(size <= sizeof(mmd_api_data_t)) || !(r_size <= size)) + return -EINVAL; + + mutex_lock_nested(&dev->bus->mdio_lock, MDIO_MUTEX_NESTED); + + if (__mxl862xx_cmd_r_valid(cmd_r)) { + /* Special handling for GET and SET command pair. */ + ret = __mxl862xx_api_wrap_cmd_r(dev, cmd, pdata, size, r_size); + goto EXIT; + } + + max = (size + 1) / 2; + data = pdata; + + /* Check whether it's worth to issue RST_DATA command. */ + for (i = cnt = 0; i < max && cnt < THR_RST_DATA; i++) { + if (!data[i]) + cnt++; + } + + ret = __wait_ctrl_busy(dev); + if (ret < 0) + goto EXIT; + + if (cnt >= THR_RST_DATA) { + /* Issue RST_DATA commdand. */ + ret = __mxl862xx_rst_data(dev); + if (ret < 0) + goto EXIT; + + for (i = 0, cnt = 0; i < max; i++) { + uint16_t off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE; + + if (i && off == 0) { + uint16_t cnt_old = cnt; + + cnt = 0; + + /* No actual data was written. */ + if (!cnt_old) + continue; + + /* Send command to set data when every + * MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs are written + * and clear the MMD register space. + */ + ret = __mxl862xx_set_data(dev, i); + if (ret < 0) + goto EXIT; + } + + /* Skip '0' data. */ + if (!data[i]) + continue; + + mxl862xx_write(dev, MXL862XX_MMD_REG_DATA_FIRST + off, + le16_to_cpu(data[i])); + cnt++; + } + } else { + for (i = 0; i < max; i++) { + uint16_t off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE; + + if (i && off == 0) { + /* Send command to set data when every + * MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs are written. + */ + ret = __mxl862xx_set_data(dev, i); + if (ret < 0) + goto EXIT; + } + + mxl862xx_write(dev, MXL862XX_MMD_REG_DATA_FIRST + off, + le16_to_cpu(data[i])); + } + } + + ret = __mxl862xx_send_cmd(dev, cmd, size, &result); + if (ret < 0) + goto EXIT; + + if (result < 0) { + ret = result; + goto EXIT; + } + + max = (r_size + 1) / 2; + for (i = 0; i < max; i++) { + uint16_t off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE; + + if (i && off == 0) { + /* Send command to fetch next batch of data + * when every MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs + * are read. + */ + ret = __mxl862xx_get_data(dev, i); + if (ret < 0) + goto EXIT; + } + + ret = mxl862xx_read(dev, MXL862XX_MMD_REG_DATA_FIRST + off); + if (ret < 0) + goto EXIT; + + if ((i * 2 + 1) == r_size) { + /* Special handling for last BYTE + * if it's not WORD aligned. + */ + *(uint8_t *)&data[i] = ret & 0xFF; + } else { + data[i] = cpu_to_le16((uint16_t)ret); + } + } + +#if defined(ENABLE_GETSET_OPT) && ENABLE_GETSET_OPT + if ((cmd != 0x1801) && (cmd != 0x1802)) + shadow.data.data[max] = 0; + memcpy(shadow.data.data, data, r_size); +#endif + + ret = result; + +EXIT: +#if defined(ENABLE_GETSET_OPT) && ENABLE_GETSET_OPT + shadow.ctrl = cmd; + shadow.ret = ret; +#endif + mutex_unlock(&dev->bus->mdio_lock); + return ret; +} diff --git a/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_host_api_impl.h b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_host_api_impl.h new file mode 100644 index 000000000000..707700c2e121 --- /dev/null +++ b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_host_api_impl.h @@ -0,0 +1,31 @@ +// spdx-license-identifier: gpl-2.0 +/* + * drivers/net/dsa/host_api/mxl862xx_host_api_impl.h - dsa driver for maxlinear mxl862xx switch chips family + * + * copyright (c) 2024 maxlinear inc. + * + * this program is free software; you can redistribute it and/or + * modify it under the terms of the gnu general public license + * as published by the free software foundation; either version 2 + * of the license, or (at your option) any later version. + * + * this program is distributed in the hope that it will be useful, + * but without any warranty; without even the implied warranty of + * merchantability or fitness for a particular purpose. see the + * gnu general public license for more details. + * + * you should have received a copy of the gnu general public license + * along with this program; if not, write to the free software + * foundation, inc., 51 franklin street, fifth floor, boston, ma 02110-1301, usa. + * + */ + +#ifndef _MXL862XX_HOST_API_IMPL_H_ +#define _MXL862XX_HOST_API_IMPL_H_ + +#include "mxl862xx_types.h" + +extern int mxl862xx_api_wrap(const mxl862xx_device_t *dev, uint16_t cmd, void *pdata, + uint16_t size, uint16_t cmd_r, uint16_t r_size); + +#endif /* _MXL862XX_HOST_API_IMPL_H_ */ diff --git a/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_mdio_relay.c b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_mdio_relay.c new file mode 100644 index 000000000000..03702d89a891 --- /dev/null +++ b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_mdio_relay.c @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * drivers/net/dsa/host_api/mxl862xx_mdio_relay.c - dsa driver for Maxlinear mxl862xx switch chips family + * + * Copyright (C) 2024 MaxLinear Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include "mxl862xx_mdio_relay.h" +#include "mxl862xx_host_api_impl.h" +#include "mxl862xx_mmd_apis.h" + +int int_gphy_read(const mxl862xx_device_t *dev, struct mdio_relay_data *parm) +{ + return mxl862xx_api_wrap(dev, INT_GPHY_READ, parm, sizeof(*parm), 0, + sizeof(parm->data)); +} + +int int_gphy_write(const mxl862xx_device_t *dev, struct mdio_relay_data *parm) +{ + return mxl862xx_api_wrap(dev, INT_GPHY_WRITE, parm, sizeof(*parm), 0, 0); +} + +int int_gphy_mod(const mxl862xx_device_t *dev, struct mdio_relay_mod_data *parm) +{ + return mxl862xx_api_wrap(dev, INT_GPHY_MOD, parm, sizeof(*parm), 0, 0); +} + +int ext_mdio_read(const mxl862xx_device_t *dev, struct mdio_relay_data *parm) +{ + return mxl862xx_api_wrap(dev, EXT_MDIO_READ, parm, sizeof(*parm), 0, + sizeof(parm->data)); +} + +int ext_mdio_write(const mxl862xx_device_t *dev, struct mdio_relay_data *parm) +{ + return mxl862xx_api_wrap(dev, EXT_MDIO_WRITE, parm, sizeof(*parm), 0, 0); +} + +int ext_mdio_mod(const mxl862xx_device_t *dev, struct mdio_relay_mod_data *parm) +{ + return mxl862xx_api_wrap(dev, EXT_MDIO_MOD, parm, sizeof(*parm), 0, 0); +} + +EXPORT_SYMBOL(int_gphy_write); +EXPORT_SYMBOL(int_gphy_read); +EXPORT_SYMBOL(int_gphy_mod); diff --git a/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_mdio_relay.h b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_mdio_relay.h new file mode 100644 index 000000000000..5e4694798ed8 --- /dev/null +++ b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_mdio_relay.h @@ -0,0 +1,86 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * drivers/net/dsa/host_api/mxl862xx_mdio_relay.h - Header file for DSA Driver for MaxLinear Mxl862xx switch chips family + * + * Copyright (C) 2024 MaxLinear Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef _MXL862XX_MDIO_RELAY_H_ +#define _MXL862XX_MDIO_RELAY_H_ + +#include +#include "mxl862xx_types.h" + +#pragma pack(push, 1) +#pragma scalar_storage_order little-endian + +struct mdio_relay_data { + /* data to be read or written */ + uint16_t data; + /* PHY index (0~7) for internal PHY + * PHY address (0~31) for external PHY access via MDIO bus + */ + uint8_t phy; + /* MMD device (0~31) */ + uint8_t mmd; + /* Register Index + * 0~31 if mmd is 0 (CL22) + * 0~65535 otherwise (CL45) + */ + uint16_t reg; +}; + +struct mdio_relay_mod_data { + /* data to be written with mask */ + uint16_t data; + /* PHY index (0~7) for internal PHY + * PHY address (0~31) for external PHY access via MDIO bus + */ + uint8_t phy; + /* MMD device (0~31) */ + uint8_t mmd; + /* Register Index + * 0~31 if mmd is 0 (CL22) + * 0~65535 otherwise (CL45) + */ + uint16_t reg; + /* mask of bit fields to be updated + * 1 to write the bit + * 0 to ignore + */ + uint16_t mask; +}; + +#pragma scalar_storage_order default +#pragma pack(pop) + +/* read internal GPHY MDIO/MMD registers */ +int int_gphy_read(const mxl862xx_device_t *dev, struct mdio_relay_data *pdata); +/* write internal GPHY MDIO/MMD registers */ +int int_gphy_write(const mxl862xx_device_t *dev, struct mdio_relay_data *pdata); +/* modify internal GPHY MDIO/MMD registers */ +int int_gphy_mod(const mxl862xx_device_t *dev, struct mdio_relay_mod_data *pdata); + +/* read external GPHY MDIO/MMD registers via MDIO bus */ +int ext_mdio_read(const mxl862xx_device_t *dev, struct mdio_relay_data *pdata); +/* write external GPHY MDIO/MMD registers via MDIO bus */ +int ext_mdio_write(const mxl862xx_device_t *dev, struct mdio_relay_data *pdata); +/* modify external GPHY MDIO/MMD registers via MDIO bus */ +int ext_mdio_mod(const mxl862xx_device_t *dev, struct mdio_relay_mod_data *pdata); + +#endif /* _MXL862XX_MDIO_RELAY_H_ */ diff --git a/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_mmd_apis.h b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_mmd_apis.h new file mode 100644 index 000000000000..6f589744c22f --- /dev/null +++ b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_mmd_apis.h @@ -0,0 +1,279 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * drivers/net/dsa/host_api/mxl862xx_mmd_apis.h - Header file for DSA Driver for MaxLinear Mxl862xx switch chips family + * + * Copyright (C) 2024 MaxLinear Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef _MXL862XX_MMD_APIS_H_ +#define _MXL862XX_MMD_APIS_H_ + +#include "mxl862xx_api.h" +#include "mxl862xx_mdio_relay.h" + +#define MXL862XX_MMD_DEV 30 +#define MXL862XX_MMD_REG_CTRL 0 +#define MXL862XX_MMD_REG_LEN_RET 1 +#define MXL862XX_MMD_REG_DATA_FIRST 2 +#define MXL862XX_MMD_REG_DATA_LAST 95 +#define MXL862XX_MMD_REG_DATA_MAX_SIZE \ + (MXL862XX_MMD_REG_DATA_LAST - MXL862XX_MMD_REG_DATA_FIRST + 1) + +typedef union mmd_api_data { + uint16_t data[MXL862XX_MMD_REG_DATA_MAX_SIZE * 3]; //Maximum data size is GSW_PCE_rule_t (508) + mxl862xx_register_mod_t mxl862xx_register_mod_t_data; + mxl862xx_cpu_port_cfg_t mxl862xx_cpu_port_cfg_t_data; + mxl862xx_port_link_cfg_t mxl862xx_port_link_cfg_t_data; + mxl862xx_port_cfg_t mxl862xx_port_cfg_t_data; + mxl862xx_bridge_alloc_t mxl862xx_bridge_alloc_t_data; + mxl862xx_bridge_port_config_t mxl862xx_bridge_port_config_t_data; + mxl862xx_debug_rmon_port_cnt_t mxl862xx_debug_rmon_port_cnt_t_data; + mxl862xx_mac_table_clear_cond_t mxl862xx_mac_table_clear_cond_t_data; + mxl862xx_mac_table_read_t mxl862xx_mac_table_read_t_data; + mxl862xx_mac_table_add_t mxl862xx_mac_table_add_t_data; + mxl862xx_mac_table_remove_t mxl862xx_mac_table_remove_t_data; + mxl862xx_stp_port_cfg_t mxl862xx_stp_port_cfg_t_data; + mxl862xx_ss_sp_tag_t mxl862xx_ss_sp_tag_t_data; + mxl862xx_monitor_port_cfg_t mxl862xx_monitor_port_cfg_t_data; + mxl862xx_ctp_port_config_t mxl862xx_ctp_port_config_t_data; + + struct mdio_relay_data mdio_relay_data; + struct mdio_relay_mod_data mdio_relay_mod_data; + struct sys_fw_image_version img_ver_data; +#ifdef CONFIG_SENSOR_MXL + struct sys_sensor_value pvt_sensor_data; +#endif +} mmd_api_data_t; + +#define MXL862XX_COMMON_MAGIC 0x100 +#define MXL862XX_TFLOW_MAGIC 0x200 +#define MXL862XX_BRDG_MAGIC 0x300 +#define MXL862XX_BRDGPORT_MAGIC 0x400 +#define MXL862XX_CTP_MAGIC 0x500 +#define MXL862XX_QOS_MAGIC 0x600 +#define MXL862XX_RMON_MAGIC 0x700 +#define MXL862XX_DEBUG_MAGIC 0x800 +#define MXL862XX_PMAC_MAGIC 0x900 +#define MXL862XX_SWMAC_MAGIC 0xA00 +#define MXL862XX_EXTVLAN_MAGIC 0xB00 +#define MXL862XX_VLANFILTER_MAGIC 0xC00 +#define MXL862XX_MULTICAST_MAGIC 0xD00 +#define MXL862XX_TRUNKING_MAGIC 0xE00 +#define MXL862XX_STP_MAGIC 0xF00 +#define MXL862XX_PBB_MAGIC 0x1000 +#define MXL862XX_VLAN_RMON_MAGIC 0x1100 +#define MXL862XX_SS_MAGIC 0x1600 + +#define GPY_GPY2XX_MAGIC 0x1800 + +#define SYS_MISC_MAGIC 0x1900 + +#ifdef MMD_API_TEST +#define MMD_API_SIMPLE_TEST (0x0 + 0x1) +#endif +#define MMD_API_SET_DATA_0 (0x0 + 0x2) +#define MMD_API_SET_DATA_1 (0x0 + 0x3) +#define MMD_API_SET_DATA_2 (0x0 + 0x4) +#define MMD_API_GET_DATA_0 (0x0 + 0x5) +#define MMD_API_GET_DATA_1 (0x0 + 0x6) +#define MMD_API_GET_DATA_2 (0x0 + 0x7) +#define MMD_API_RST_DATA (0x0 + 0x8) + +#define MXL862XX_COMMON_REGISTERGET (MXL862XX_COMMON_MAGIC + 0x1) +#define MXL862XX_COMMON_REGISTERSET (MXL862XX_COMMON_MAGIC + 0x2) +#define MXL862XX_COMMON_CPU_PORTCFGGET (MXL862XX_COMMON_MAGIC + 0x3) +#define MXL862XX_COMMON_CPU_PORTCFGSET (MXL862XX_COMMON_MAGIC + 0x4) +#define MXL862XX_COMMON_PORTLINKCFGGET (MXL862XX_COMMON_MAGIC + 0x5) +#define MXL862XX_COMMON_PORTLINKCFGSET (MXL862XX_COMMON_MAGIC + 0x6) +#define MXL862XX_COMMON_PORTCFGGET (MXL862XX_COMMON_MAGIC + 0x7) +#define MXL862XX_COMMON_PORTCFGSET (MXL862XX_COMMON_MAGIC + 0x8) +#define MXL862XX_COMMON_CFGGET (MXL862XX_COMMON_MAGIC + 0x9) +#define MXL862XX_COMMON_CFGSET (MXL862XX_COMMON_MAGIC + 0xA) +#define MXL862XX_COMMON_MONITORPORTCFGGET (MXL862XX_COMMON_MAGIC + 0xD) +#define MXL862XX_COMMON_MONITORPORTCFGSET (MXL862XX_COMMON_MAGIC + 0xE) +#define MXL862XX_COMMON_FREEZE (MXL862XX_COMMON_MAGIC + 0xF) +#define MXL862XX_COMMON_UNFREEZE (MXL862XX_COMMON_MAGIC + 0x10) +#define MXL862XX_COMMON_REGISTERMOD (MXL862XX_COMMON_MAGIC + 0x11) + +#define MXL862XX_TFLOW_PCERULEREAD (MXL862XX_TFLOW_MAGIC + 0x1) +#define MXL862XX_TFLOW_PCERULEWRITE (MXL862XX_TFLOW_MAGIC + 0x2) +#define MXL862XX_TFLOW_PCERULEDELETE (MXL862XX_TFLOW_MAGIC + 0x3) +#define MXL862XX_TFLOW_PCERULEALLOC (MXL862XX_TFLOW_MAGIC + 0x4) +#define MXL862XX_TFLOW_PCERULEFREE (MXL862XX_TFLOW_MAGIC + 0x5) +#define MXL862XX_TFLOW_PCERULEENABLE (MXL862XX_TFLOW_MAGIC + 0x6) +#define MXL862XX_TFLOW_PCERULEDISABLE (MXL862XX_TFLOW_MAGIC + 0x7) + +#define MXL862XX_BRIDGE_ALLOC (MXL862XX_BRDG_MAGIC + 0x1) +#define MXL862XX_BRIDGE_CONFIGSET (MXL862XX_BRDG_MAGIC + 0x2) +#define MXL862XX_BRIDGE_CONFIGGET (MXL862XX_BRDG_MAGIC + 0x3) +#define MXL862XX_BRIDGE_FREE (MXL862XX_BRDG_MAGIC + 0x4) + +#define MXL862XX_BRIDGEPORT_ALLOC (MXL862XX_BRDGPORT_MAGIC + 0x1) +#define MXL862XX_BRIDGEPORT_CONFIGSET (MXL862XX_BRDGPORT_MAGIC + 0x2) +#define MXL862XX_BRIDGEPORT_CONFIGGET (MXL862XX_BRDGPORT_MAGIC + 0x3) +#define MXL862XX_BRIDGEPORT_FREE (MXL862XX_BRDGPORT_MAGIC + 0x4) + +#define MXL862XX_CTP_PORTASSIGNMENTALLOC (MXL862XX_CTP_MAGIC + 0x1) +#define MXL862XX_CTP_PORTASSIGNMENTFREE (MXL862XX_CTP_MAGIC + 0x2) +#define MXL862XX_CTP_PORTASSIGNMENTSET (MXL862XX_CTP_MAGIC + 0x3) +#define MXL862XX_CTP_PORTASSIGNMENTGET (MXL862XX_CTP_MAGIC + 0x4) +#define MXL862XX_CTP_PORTCONFIGSET (MXL862XX_CTP_MAGIC + 0x5) +#define MXL862XX_CTP_PORTCONFIGGET (MXL862XX_CTP_MAGIC + 0x6) +#define MXL862XX_CTP_PORTCONFIGRESET (MXL862XX_CTP_MAGIC + 0x7) + +#define MXL862XX_QOS_METERCFGGET (MXL862XX_QOS_MAGIC + 0x1) +#define MXL862XX_QOS_METERCFGSET (MXL862XX_QOS_MAGIC + 0x2) +#define MXL862XX_QOS_DSCP_CLASSGET (MXL862XX_QOS_MAGIC + 0x4) +#define MXL862XX_QOS_DSCP_CLASSSET (MXL862XX_QOS_MAGIC + 0x5) +#define MXL862XX_QOS_DSCP_DROPPRECEDENCECFGGET (MXL862XX_QOS_MAGIC + 0x6) +#define MXL862XX_QOS_DSCP_DROPPRECEDENCECFGSET (MXL862XX_QOS_MAGIC + 0x7) +#define MXL862XX_QOS_PORTREMARKINGCFGGET (MXL862XX_QOS_MAGIC + 0x8) +#define MXL862XX_QOS_PORTREMARKINGCFGSET (MXL862XX_QOS_MAGIC + 0x9) +#define MXL862XX_QOS_PCP_CLASSGET (MXL862XX_QOS_MAGIC + 0xA) +#define MXL862XX_QOS_PCP_CLASSSET (MXL862XX_QOS_MAGIC + 0xB) +#define MXL862XX_QOS_PORTCFGGET (MXL862XX_QOS_MAGIC + 0xC) +#define MXL862XX_QOS_PORTCFGSET (MXL862XX_QOS_MAGIC + 0xD) +#define MXL862XX_QOS_QUEUEPORTGET (MXL862XX_QOS_MAGIC + 0xE) +#define MXL862XX_QOS_QUEUEPORTSET (MXL862XX_QOS_MAGIC + 0xF) +#define MXL862XX_QOS_SCHEDULERCFGGET (MXL862XX_QOS_MAGIC + 0x10) +#define MXL862XX_QOS_SCHEDULERCFGSET (MXL862XX_QOS_MAGIC + 0x11) +#define MXL862XX_QOS_SHAPERCFGGET (MXL862XX_QOS_MAGIC + 0x12) +#define MXL862XX_QOS_SHAPERCFGSET (MXL862XX_QOS_MAGIC + 0x13) +#define MXL862XX_QOS_SHAPERQUEUEASSIGN (MXL862XX_QOS_MAGIC + 0x14) +#define MXL862XX_QOS_SHAPERQUEUEDEASSIGN (MXL862XX_QOS_MAGIC + 0x15) +#define MXL862XX_QOS_SHAPERQUEUEGET (MXL862XX_QOS_MAGIC + 0x16) +#define MXL862XX_QOS_STORMCFGSET (MXL862XX_QOS_MAGIC + 0x17) +#define MXL862XX_QOS_STORMCFGGET (MXL862XX_QOS_MAGIC + 0x18) +#define MXL862XX_QOS_WREDCFGGET (MXL862XX_QOS_MAGIC + 0x19) +#define MXL862XX_QOS_WREDCFGSET (MXL862XX_QOS_MAGIC + 0x1A) +#define MXL862XX_QOS_WREDQUEUECFGGET (MXL862XX_QOS_MAGIC + 0x1B) +#define MXL862XX_QOS_WREDQUEUECFGSET (MXL862XX_QOS_MAGIC + 0x1C) +#define MXL862XX_QOS_WREDPORTCFGGET (MXL862XX_QOS_MAGIC + 0x1D) +#define MXL862XX_QOS_WREDPORTCFGSET (MXL862XX_QOS_MAGIC + 0x1E) +#define MXL862XX_QOS_FLOWCTRLCFGGET (MXL862XX_QOS_MAGIC + 0x1F) +#define MXL862XX_QOS_FLOWCTRLCFGSET (MXL862XX_QOS_MAGIC + 0x20) +#define MXL862XX_QOS_FLOWCTRLPORTCFGGET (MXL862XX_QOS_MAGIC + 0x21) +#define MXL862XX_QOS_FLOWCTRLPORTCFGSET (MXL862XX_QOS_MAGIC + 0x22) +#define MXL862XX_QOS_QUEUEBUFFERRESERVECFGGET (MXL862XX_QOS_MAGIC + 0x23) +#define MXL862XX_QOS_QUEUEBUFFERRESERVECFGSET (MXL862XX_QOS_MAGIC + 0x24) +#define MXL862XX_QOS_COLORMARKINGTABLEGET (MXL862XX_QOS_MAGIC + 0x26) +#define MXL862XX_QOS_COLORMARKINGTABLESET (MXL862XX_QOS_MAGIC + 0x27) +#define MXL862XX_QOS_COLORREMARKINGTABLESET (MXL862XX_QOS_MAGIC + 0x28) +#define MXL862XX_QOS_COLORREMARKINGTABLEGET (MXL862XX_QOS_MAGIC + 0x29) +#define MXL862XX_QOS_METERALLOC (MXL862XX_QOS_MAGIC + 0x2A) +#define MXL862XX_QOS_METERFREE (MXL862XX_QOS_MAGIC + 0x2B) +#define MXL862XX_QOS_DSCP2PCPTABLESET (MXL862XX_QOS_MAGIC + 0x2C) +#define MXL862XX_QOS_DSCP2PCPTABLEGET (MXL862XX_QOS_MAGIC + 0x2D) +#define MXL862XX_QOS_PMAPPERTABLESET (MXL862XX_QOS_MAGIC + 0x2E) +#define MXL862XX_QOS_PMAPPERTABLEGET (MXL862XX_QOS_MAGIC + 0x2F) +#define MXL862XX_QOS_SVLAN_PCP_CLASSGET (MXL862XX_QOS_MAGIC + 0x30) +#define MXL862XX_QOS_SVLAN_PCP_CLASSSET (MXL862XX_QOS_MAGIC + 0x31) + +#define MXL862XX_RMON_PORT_GET (MXL862XX_RMON_MAGIC + 0x1) +#define MXL862XX_RMON_MODE_SET (MXL862XX_RMON_MAGIC + 0x2) +#define MXL862XX_RMON_METER_GET (MXL862XX_RMON_MAGIC + 0x3) +#define MXL862XX_RMON_CLEAR (MXL862XX_RMON_MAGIC + 0x4) +#define MXL862XX_RMON_TFLOWGET (MXL862XX_RMON_MAGIC + 0x5) +#define MXL862XX_RMON_TFLOWCLEAR (MXL862XX_RMON_MAGIC + 0x6) +#define MXL862XX_RMON_TFLOWCOUNTMODESET (MXL862XX_RMON_MAGIC + 0x7) +#define MXL862XX_RMON_TFLOWCOUNTMODEGET (MXL862XX_RMON_MAGIC + 0x8) + +#define MXL862XX_DEBUG_RMON_PORT_GET (MXL862XX_DEBUG_MAGIC + 0x1) + +#define MXL862XX_PMAC_COUNTGET (MXL862XX_PMAC_MAGIC + 0x1) +#define MXL862XX_PMAC_GBL_CFGSET (MXL862XX_PMAC_MAGIC + 0x2) +#define MXL862XX_PMAC_GBL_CFGGET (MXL862XX_PMAC_MAGIC + 0x3) +#define MXL862XX_PMAC_BM_CFGSET (MXL862XX_PMAC_MAGIC + 0x4) +#define MXL862XX_PMAC_BM_CFGGET (MXL862XX_PMAC_MAGIC + 0x5) +#define MXL862XX_PMAC_IG_CFGSET (MXL862XX_PMAC_MAGIC + 0x6) +#define MXL862XX_PMAC_IG_CFGGET (MXL862XX_PMAC_MAGIC + 0x7) +#define MXL862XX_PMAC_EG_CFGSET (MXL862XX_PMAC_MAGIC + 0x8) +#define MXL862XX_PMAC_EG_CFGGET (MXL862XX_PMAC_MAGIC + 0x9) + +#define MXL862XX_MAC_TABLECLEAR (MXL862XX_SWMAC_MAGIC + 0x1) +#define MXL862XX_MAC_TABLEENTRYADD (MXL862XX_SWMAC_MAGIC + 0x2) +#define MXL862XX_MAC_TABLEENTRYREAD (MXL862XX_SWMAC_MAGIC + 0x3) +#define MXL862XX_MAC_TABLEENTRYQUERY (MXL862XX_SWMAC_MAGIC + 0x4) +#define MXL862XX_MAC_TABLEENTRYREMOVE (MXL862XX_SWMAC_MAGIC + 0x5) +#define MXL862XX_MAC_DEFAULTFILTERSET (MXL862XX_SWMAC_MAGIC + 0x6) +#define MXL862XX_MAC_DEFAULTFILTERGET (MXL862XX_SWMAC_MAGIC + 0x7) +#define MXL862XX_MAC_TABLECLEARCOND (MXL862XX_SWMAC_MAGIC + 0x8) + +#define MXL862XX_EXTENDEDVLAN_ALLOC (MXL862XX_EXTVLAN_MAGIC + 0x1) +#define MXL862XX_EXTENDEDVLAN_SET (MXL862XX_EXTVLAN_MAGIC + 0x2) +#define MXL862XX_EXTENDEDVLAN_GET (MXL862XX_EXTVLAN_MAGIC + 0x3) +#define MXL862XX_EXTENDEDVLAN_FREE (MXL862XX_EXTVLAN_MAGIC + 0x4) + +#define MXL862XX_VLANFILTER_ALLOC (MXL862XX_VLANFILTER_MAGIC + 0x1) +#define MXL862XX_VLANFILTER_SET (MXL862XX_VLANFILTER_MAGIC + 0x2) +#define MXL862XX_VLANFILTER_GET (MXL862XX_VLANFILTER_MAGIC + 0x3) +#define MXL862XX_VLANFILTER_FREE (MXL862XX_VLANFILTER_MAGIC + 0x4) + +#define MXL862XX_VLAN_COUNTER_MAPPING_SET (MXL862XX_VLAN_RMON_MAGIC + 0x1) +#define MXL862XX_VLAN_COUNTER_MAPPING_GET (MXL862XX_VLAN_RMON_MAGIC + 0x2) +#define MXL862XX_VLAN_RMON_GET (MXL862XX_VLAN_RMON_MAGIC + 0x3) +#define MXL862XX_VLAN_RMON_CLEAR (MXL862XX_VLAN_RMON_MAGIC + 0x4) +#define MXL862XX_VLAN_RMON_CONTROL_SET (MXL862XX_VLAN_RMON_MAGIC + 0x5) +#define MXL862XX_VLAN_RMON_CONTROL_GET (MXL862XX_VLAN_RMON_MAGIC + 0x6) + +#define MXL862XX_MULTICAST_ROUTERPORTADD (MXL862XX_MULTICAST_MAGIC + 0x1) +#define MXL862XX_MULTICAST_ROUTERPORTREAD (MXL862XX_MULTICAST_MAGIC + 0x2) +#define MXL862XX_MULTICAST_ROUTERPORTREMOVE (MXL862XX_MULTICAST_MAGIC + 0x3) +#define MXL862XX_MULTICAST_SNOOPCFGGET (MXL862XX_MULTICAST_MAGIC + 0x4) +#define MXL862XX_MULTICAST_SNOOPCFGSET (MXL862XX_MULTICAST_MAGIC + 0x5) +#define MXL862XX_MULTICAST_TABLEENTRYADD (MXL862XX_MULTICAST_MAGIC + 0x6) +#define MXL862XX_MULTICAST_TABLEENTRYREAD (MXL862XX_MULTICAST_MAGIC + 0x7) +#define MXL862XX_MULTICAST_TABLEENTRYREMOVE (MXL862XX_MULTICAST_MAGIC + 0x8) + +#define MXL862XX_TRUNKING_CFGGET (MXL862XX_TRUNKING_MAGIC + 0x1) +#define MXL862XX_TRUNKING_CFGSET (MXL862XX_TRUNKING_MAGIC + 0x2) +#define MXL862XX_TRUNKING_PORTCFGGET (MXL862XX_TRUNKING_MAGIC + 0x3) +#define MXL862XX_TRUNKING_PORTCFGSET (MXL862XX_TRUNKING_MAGIC + 0x4) + +#define MXL862XX_STP_PORTCFGGET (MXL862XX_STP_MAGIC + 0x1) +#define MXL862XX_STP_PORTCFGSET (MXL862XX_STP_MAGIC + 0x2) +#define MXL862XX_STP_BPDU_RULEGET (MXL862XX_STP_MAGIC + 0x3) +#define MXL862XX_STP_BPDU_RULESET (MXL862XX_STP_MAGIC + 0x4) + +#define MXL862XX_PBB_TEMPLATEALLOC (MXL862XX_PBB_MAGIC + 0x1) +#define MXL862XX_PBB_TEMPLATEFREE (MXL862XX_PBB_MAGIC + 0x2) +#define MXL862XX_PBB_TEMPLATESET (MXL862XX_PBB_MAGIC + 0x3) +#define MXL862XX_PBB_TEMPLATEGET (MXL862XX_PBB_MAGIC + 0x4) + +#define MXL862XX_SS_SPTAG_GET (MXL862XX_SS_MAGIC + 0x01) +#define MXL862XX_SS_SPTAG_SET (MXL862XX_SS_MAGIC + 0x02) + +#define INT_GPHY_READ (GPY_GPY2XX_MAGIC + 0x01) +#define INT_GPHY_WRITE (GPY_GPY2XX_MAGIC + 0x02) +#define INT_GPHY_MOD (GPY_GPY2XX_MAGIC + 0x03) +#define EXT_MDIO_READ (GPY_GPY2XX_MAGIC + 0x11) +#define EXT_MDIO_WRITE (GPY_GPY2XX_MAGIC + 0x12) +#define EXT_MDIO_MOD (GPY_GPY2XX_MAGIC + 0x13) + +#define SYS_MISC_FW_UPDATE (SYS_MISC_MAGIC + 0x01) +#define SYS_MISC_FW_VERSION (SYS_MISC_MAGIC + 0x02) +#define SYS_MISC_PVT_TEMP (SYS_MISC_MAGIC + 0x03) +#define SYS_MISC_PVT_VOLTAGE (SYS_MISC_MAGIC + 0x04) + +#define MMD_API_MAXIMUM_ID 0x7FFF + +int mxl862xx_read(const mxl862xx_device_t *dev, uint32_t regaddr); +int mxl862xx_write(const mxl862xx_device_t *dev, uint32_t regaddr, uint16_t data); + + +#endif /* _MXL862XX_MMD_APIS_H_ */ diff --git a/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_rmon.h b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_rmon.h new file mode 100644 index 000000000000..c4d3a63c488a --- /dev/null +++ b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_rmon.h @@ -0,0 +1,509 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * drivers/net/dsa/host_api/mxl862xx_rmon.h - dsa driver for Maxlinear mxl862xx switch chips family + * + * Copyright (C) 2024 MaxLinear Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef _MXL862XX_RMON_H_ +#define _MXL862XX_RMON_H_ + +#include "mxl862xx_types.h" + +#pragma pack(push, 1) +#pragma scalar_storage_order little-endian + +/** \brief Port Type - GSWIP-3.1 only. + Used by \ref MXL862XX_port_cfg_t. */ +typedef enum { + /** Logical Port */ + MXL862XX_LOGICAL_PORT = 0, + /** Physical Port + Applicable only for GSWIP-3.1/3.2 */ + MXL862XX_PHYSICAL_PORT = 1, + /** Connectivity Termination Port (CTP) + Applicable only for GSWIP-3.1/3.2 */ + MXL862XX_CTP_PORT = 2, + /** Bridge Port + Applicable only for GSWIP-3.1/3.2 */ + MXL862XX_BRIDGE_PORT = 3 +} mxl862xx_port_type_t; + +/**Defined as per RMON counter table structure + Applicable only for GSWIP 3.1*/ +typedef enum { + MXL862XX_RMON_CTP_PORT_RX = 0, + MXL862XX_RMON_CTP_PORT_TX = 1, + MXL862XX_RMON_BRIDGE_PORT_RX = 2, + MXL862XX_RMON_BRIDGE_PORT_TX = 3, + MXL862XX_RMON_CTP_PORT_PCE_BYPASS = 4, + MXL862XX_RMON_TFLOW_RX = 5, + MXL862XX_RMON_TFLOW_TX = 6, + MXL862XX_RMON_QMAP = 0x0E, + MXL862XX_RMON_METER = 0x19, + MXL862XX_RMON_PMAC = 0x1C, +} mxl862xx_rmon_port_type_t; + +/** Defined as per RMON counter table structure + * Applicable only for GSWIP 3.0 + */ +typedef enum { + MXL862XX_RMON_REDIRECTION = 0X18, + MXL862XX_RMON_IF = 0x1A, + MXL862XX_RMON_ROUTE = 0x1B, + MXL862XX_RMON_PMACIG = 0x1C, +} mxl862xx_rmon_port_t; + +typedef enum { + /** Parser microcode table */ + PMAC_BPMAP_INDEX = 0x00, + PMAC_IGCFG_INDEX = 0x01, + PMAC_EGCFG_INDEX = 0x02, +} pm_tbl_cmds_t; +/** \brief RMON Counters Type enumeration. + Used by \ref MXL862XX_RMON_clear_t and \ref MXL862XX_RMON_mode_t. */ +typedef enum { + /** All RMON Types Counters */ + MXL862XX_RMON_ALL_TYPE = 0, + /** All PMAC RMON Counters */ + MXL862XX_RMON_PMAC_TYPE = 1, + /** Port based RMON Counters */ + MXL862XX_RMON_PORT_TYPE = 2, + /** Meter based RMON Counters */ + MXL862XX_RMON_METER_TYPE = 3, + /** Interface based RMON Counters */ + MXL862XX_RMON_IF_TYPE = 4, + /** Route based RMON Counters */ + MXL862XX_RMON_ROUTE_TYPE = 5, + /** Redirected Traffic based RMON Counters */ + MXL862XX_RMON_REDIRECT_TYPE = 6, + /** Bridge Port based RMON Counters */ + MXL862XX_RMON_BRIDGE_TYPE = 7, + /** CTP Port based RMON Counters */ + MXL862XX_RMON_CTP_TYPE = 8, +} mxl862xx_rmon_type_t; + +/** \brief RMON Counters Mode Enumeration. + This enumeration defines Counters mode - Packets based or Bytes based counting. + Metering and Routing Sessions RMON counting support either Byte based or packets based only. */ +typedef enum { + /** Packet based RMON Counters */ + MXL862XX_RMON_COUNT_PKTS = 0, + /** Bytes based RMON Counters */ + MXL862XX_RMON_COUNT_BYTES = 1, + /** number of dropped frames, supported only for interface cunters */ + MXL862XX_RMON_DROP_COUNT = 2, +} mxl862xx_rmon_count_mode_t; + +/** \brief Used for getting metering RMON counters. + Used by \ref MXL862XX_RMON_METER_GET */ +typedef enum { + /* Resereved */ + MXL862XX_RMON_METER_COLOR_RES = 0, + /* Green color */ + MXL862XX_RMON_METER_COLOR_GREEN = 1, + /* Yellow color */ + MXL862XX_RMON_METER_COLOR_YELLOW = 2, + /* Red color */ + MXL862XX_RMON_METER_COLOR_RED = 3, +} mxl862xx_rmon_meter_color_t; + +/* TFLOW counter type */ +typedef enum { + /* Set all Rx/Tx/PCE-Bp-Tx registers to same value */ + MXL862XX_TFLOW_COUNTER_ALL = 0, //Default for 'set' function. + /* SEt PCE Rx register config only */ + MXL862XX_TFLOW_COUNTER_PCE_Rx = 1, //Default for 'get' function. + /* SEt PCE Tx register config only */ + MXL862XX_TFLOW_COUNTER_PCE_Tx = 2, + /* SEt PCE-Bypass Tx register config only */ + MXL862XX_TFLOW_COUNTER_PCE_BP_Tx = 3, +} mxl862xx_tflow_count_conf_type_t; + +/* TFLOW counter mode type */ +typedef enum { + /* Global mode */ + MXL862XX_TFLOW_CMODE_GLOBAL = 0, + /* Logical mode */ + MXL862XX_TFLOW_CMODE_LOGICAL = 1, + /* CTP port mode */ + MXL862XX_TFLOW_CMODE_CTP = 2, + /* Bridge port mode */ + MXL862XX_TFLOW_CMODE_BRIDGE = 3, +} mxl862xx_tflow_cmode_type_t; + +/* TFLOW CTP counter LSB bits */ +typedef enum { + /* Num of valid bits */ + MXL862XX_TCM_CTP_VAL_BITS_0 = 0, + MXL862XX_TCM_CTP_VAL_BITS_1 = 1, + MXL862XX_TCM_CTP_VAL_BITS_2 = 2, + MXL862XX_TCM_CTP_VAL_BITS_3 = 3, + MXL862XX_TCM_CTP_VAL_BITS_4 = 4, + MXL862XX_TCM_CTP_VAL_BITS_5 = 5, + MXL862XX_TCM_CTP_VAL_BITS_6 = 6, +} mxl862xx_tflow_ctp_val_bits_t; + +/* TFLOW bridge port counter LSB bits */ +typedef enum { + /* Num of valid bits */ + MXL862XX_TCM_BRP_VAL_BITS_2 = 2, + MXL862XX_TCM_BRP_VAL_BITS_3 = 3, + MXL862XX_TCM_BRP_VAL_BITS_4 = 4, + MXL862XX_TCM_BRP_VAL_BITS_5 = 5, + MXL862XX_TCM_BRP_VAL_BITS_6 = 6, +} mxl862xx_tflow_brp_val_bits_t; + +/** + \brief RMON Counters for individual Port. + This structure contains the RMON counters of an Ethernet Switch Port. + Used by \ref MXL862XX_RMON_PORT_GET. */ +typedef struct { + /** Port Type. This gives information which type of port to get RMON. + port_id should be based on this field. + This is new in GSWIP-3.1. For GSWIP-2.1/2.2/3.0, this field is always + ZERO (MXL862XX_LOGICAL_PORT). */ + mxl862xx_port_type_t port_type; + /** Ethernet Port number (zero-based counting). The valid range is hardware + dependent. An error code is delivered if the selected port is not + available. This parameter specifies for which MAC port the RMON + counter is read. It has to be set by the application before + calling \ref MXL862XX_RMON_PORT_GET. */ + u16 port_id; + /** Sub interface ID group. The valid range is hardware/protocol dependent. + + \remarks + This field is valid when \ref MXL862XX_RMON_Port_cnt_t::e_port_type is + \ref MXL862XX_port_type_t::MXL862XX_CTP_PORT. + Sub interface ID group is defined for each of \ref MXL862XX_Logical_port_mode_t. + For both \ref MXL862XX_LOGICAL_PORT_8BIT_WLAN and + \ref MXL862XX_LOGICAL_PORT_9BIT_WLAN, this field is VAP. + For \ref MXL862XX_LOGICAL_PORT_GPON, this field is GEM index. + For \ref MXL862XX_LOGICAL_PORT_EPON, this field is stream index. + For \ref MXL862XX_LOGICAL_PORT_GINT, this field is LLID. + For others, this field is 0. */ + u16 sub_if_id_group; + /** Separate set of CTP Tx counters when PCE is bypassed. GSWIP-3.1 only.*/ + bool pce_bypass; + /*Applicable only for GSWIP 3.1*/ + /** Discarded at Extended VLAN Operation Packet Count. GSWIP-3.1 only. */ + u32 rx_extended_vlan_discard_pkts; + /** Discarded MTU Exceeded Packet Count. GSWIP-3.1 only. */ + u32 mtu_exceed_discard_pkts; + /** Tx Undersize (<64) Packet Count. GSWIP-3.1 only. */ + u32 tx_under_size_good_pkts; + /** Tx Oversize (>1518) Packet Count. GSWIP-3.1 only. */ + u32 tx_oversize_good_pkts; + /** Receive Packet Count (only packets that are accepted and not discarded). */ + u32 rx_good_pkts; + /** Receive Unicast Packet Count. */ + u32 rx_unicast_pkts; + /** Receive Broadcast Packet Count. */ + u32 rx_broadcast_pkts; + /** Receive Multicast Packet Count. */ + u32 rx_multicast_pkts; + /** Receive FCS Error Packet Count. */ + u32 rx_fCSError_pkts; + /** Receive Undersize Good Packet Count. */ + u32 rx_under_size_good_pkts; + /** Receive Oversize Good Packet Count. */ + u32 rx_oversize_good_pkts; + /** Receive Undersize Error Packet Count. */ + u32 rx_under_size_error_pkts; + /** Receive Good Pause Packet Count. */ + u32 rx_good_pause_pkts; + /** Receive Oversize Error Packet Count. */ + u32 rx_oversize_error_pkts; + /** Receive Align Error Packet Count. */ + u32 rx_align_error_pkts; + /** Filtered Packet Count. */ + u32 rx_filtered_pkts; + /** Receive Size 64 Bytes Packet Count. */ + u32 rx64Byte_pkts; + /** Receive Size 65-127 Bytes Packet Count. */ + u32 rx127Byte_pkts; + /** Receive Size 128-255 Bytes Packet Count. */ + u32 rx255Byte_pkts; + /** Receive Size 256-511 Bytes Packet Count. */ + u32 rx511Byte_pkts; + /** Receive Size 512-1023 Bytes Packet Count. */ + u32 rx1023Byte_pkts; + /** Receive Size 1024-1522 Bytes (or more, if configured) Packet Count. */ + u32 rx_max_byte_pkts; + /** Overall Transmit Good Packets Count. */ + u32 tx_good_pkts; + /** Transmit Unicast Packet Count. */ + u32 tx_unicast_pkts; + /** Transmit Broadcast Packet Count. */ + u32 tx_broadcast_pkts; + /** Transmit Multicast Packet Count. */ + u32 tx_multicast_pkts; + /** Transmit Single Collision Count. */ + u32 tx_single_coll_count; + /** Transmit Multiple Collision Count. */ + u32 tx_mult_coll_count; + /** Transmit Late Collision Count. */ + u32 tx_late_coll_count; + /** Transmit Excessive Collision Count. */ + u32 tx_excess_coll_count; + /** Transmit Collision Count. */ + u32 tx_coll_count; + /** Transmit Pause Packet Count. */ + u32 tx_pause_count; + /** Transmit Size 64 Bytes Packet Count. */ + u32 tx64Byte_pkts; + /** Transmit Size 65-127 Bytes Packet Count. */ + u32 tx127Byte_pkts; + /** Transmit Size 128-255 Bytes Packet Count. */ + u32 tx255Byte_pkts; + /** Transmit Size 256-511 Bytes Packet Count. */ + u32 tx511Byte_pkts; + /** Transmit Size 512-1023 Bytes Packet Count. */ + u32 tx1023Byte_pkts; + /** Transmit Size 1024-1522 Bytes (or more, if configured) Packet Count. */ + u32 tx_max_byte_pkts; + /** Transmit Drop Packet Count. */ + u32 tx_dropped_pkts; + /** Transmit Dropped Packet Count, based on Congestion Management. */ + u32 tx_acm_dropped_pkts; + /** Receive Dropped Packet Count. */ + u32 rx_dropped_pkts; + /** Receive Good Byte Count (64 bit). */ + u64 rx_good_bytes; + /** Receive Bad Byte Count (64 bit). */ + u64 rx_bad_bytes; + /** Transmit Good Byte Count (64 bit). */ + u64 tx_good_bytes; +} mxl862xx_rmon_port_cnt_t; + +/** \brief RMON Counters Mode for different Elements. + This structure takes RMON Counter Element Name and mode config */ +typedef struct { + /** RMON Counters Type */ + mxl862xx_rmon_type_t rmon_type; + /** RMON Counters Mode */ + mxl862xx_rmon_count_mode_t count_mode; +} mxl862xx_rmon_mode_t; + +/** + \brief RMON Counters for Meter - Type (GSWIP-3.0 only). + This structure contains the RMON counters of one Meter Instance. + Used by \ref MXL862XX_RMON_METER_GET. */ +typedef struct { + /** Meter Instance number (zero-based counting). The valid range is hardware + dependent. An error code is delivered if the selected meter is not + available. This parameter specifies for which Meter Id the RMON-1 + counter is read. It has to be set by the application before + calling \ref MXL862XX_RMON_METER_GET. */ + u8 meter_id; + /** Metered Green colored packets or bytes (depending upon mode) count. */ + u32 green_count; + /** Metered Yellow colored packets or bytes (depending upon mode) count. */ + u32 yellow_count; + /** Metered Red colored packets or bytes (depending upon mode) count. */ + u32 red_count; + /** Metered Reserved (Future Use) packets or bytes (depending upon mode) count. */ + u32 res_count; +} mxl862xx_rmon_meter_cnt_t; + +/** \brief RMON Counters Data Structure for clearance of values. + Used by \ref MXL862XX_RMON_CLEAR. */ +typedef struct { + /** RMON Counters Type */ + mxl862xx_rmon_type_t rmon_type; + /** RMON Counters Identifier - Meter, Port, If, Route, etc. */ + u8 rmon_id; +} mxl862xx_rmon_clear_t; + +/** + \brief Hardware platform TFLOW counter mode. + Supported modes include, Global (default), Logical, CTP, Bridge port mode. + The number of counters that can be assigned varies based these mode type. + Used by \ref MXL862XX_TFLOW_COUNT_MODE_SET and MXL862XX_TFLOW_COUNT_MODE_GET. */ +typedef struct { + //Counter type. PCE Rx/Tx/Bp-Tx. + mxl862xx_tflow_count_conf_type_t count_type; + //Counter mode. Global/Logical/CTP/br_p. + mxl862xx_tflow_cmode_type_t count_mode; + //The below params are valid only for CTP/br_p types. + //A group of ports matching MS (9-n) bits. n is n_ctp_lsb or n_brp_lsb. + u16 port_msb; + //Number of valid bits in CTP port counter mode. + mxl862xx_tflow_ctp_val_bits_t ctp_lsb; + //Number of valid bits in bridge port counter mode. + mxl862xx_tflow_brp_val_bits_t brp_lsb; +} mxl862xx_tflow_cmode_conf_t; + +/** + \brief Hardware platform extended RMON Counters. GSWIP-3.1 only. + This structure contains additional RMON counters. These counters can be + used by the packet classification engine and can be freely assigned to + dedicated packet rules and flows. + Used by \ref MXL862XX_RMON_FLOW_GET. */ +typedef struct { + /** If TRUE, use \ref MXL862XX_RMON_flow_get_t::n_index to access the Flow Counter, + otherwise, use \ref MXL862XX_TFLOW_COUNT_MODE_GET to determine mode and use + \ref MXL862XX_RMON_flow_get_t::port_id and \ref MXL862XX_RMON_flow_get_t::flow_id + to calculate index of the Flow Counter. */ + bool use_index; + /** Absolute index of Flow Counter. */ + u16 index; + /** Port ID. This could be Logical Port, CTP or Bridge Port. It depends + on the mode set by \ref MXL862XX_TFLOW_COUNT_MODE_SET. */ + u16 port_id; + /** \ref MXL862XX_PCE_action_t::flow_id. The range depends on the mode set + by \ref MXL862XX_TFLOW_COUNT_MODE_SET. */ + u16 flow_id; + + /** Rx Packet Counter */ + u32 rx_pkts; + /** Tx Packet Counter (non-PCE-Bypass) */ + u32 tx_pkts; + /** Tx Packet Counter (PCE-Bypass) */ + u32 tx_pce_bypass_pkts; +} mxl862xx_rmon_flow_get_t; + +/** \brief For debugging Purpose only. + Used for GSWIP 3.1. */ + +typedef struct { + /** Ethernet Port number (zero-based counting). The valid range is hardware + dependent. An error code is delivered if the selected port is not + available. This parameter specifies for which MAC port the RMON + counter is read. It has to be set by the application before + calling \ref MXL862XX_RMON_PORT_GET. */ + u16 port_id; + /**Table address selection based on port type + Applicable only for GSWIP 3.1 + \ref MXL862XX_RMON_port_type_t**/ + mxl862xx_rmon_port_type_t port_type; + /*Applicable only for GSWIP 3.1*/ + u32 rx_extended_vlan_discard_pkts; + /*Applicable only for GSWIP 3.1*/ + u32 mtu_exceed_discard_pkts; + /*Applicable only for GSWIP 3.1*/ + u32 tx_under_size_good_pkts; + /*Applicable only for GSWIP 3.1*/ + u32 tx_oversize_good_pkts; + /** Receive Packet Count (only packets that are accepted and not discarded). */ + u32 rx_good_pkts; + /** Receive Unicast Packet Count. */ + u32 rx_unicast_pkts; + /** Receive Broadcast Packet Count. */ + u32 rx_broadcast_pkts; + /** Receive Multicast Packet Count. */ + u32 rx_multicast_pkts; + /** Receive FCS Error Packet Count. */ + u32 rx_fcserror_pkts; + /** Receive Undersize Good Packet Count. */ + u32 rx_under_size_good_pkts; + /** Receive Oversize Good Packet Count. */ + u32 rx_oversize_good_pkts; + /** Receive Undersize Error Packet Count. */ + u32 rx_under_size_error_pkts; + /** Receive Good Pause Packet Count. */ + u32 rx_good_pause_pkts; + /** Receive Oversize Error Packet Count. */ + u32 rx_oversize_error_pkts; + /** Receive Align Error Packet Count. */ + u32 rx_align_error_pkts; + /** Filtered Packet Count. */ + u32 rx_filtered_pkts; + /** Receive Size 64 Bytes Packet Count. */ + u32 rx64byte_pkts; + /** Receive Size 65-127 Bytes Packet Count. */ + u32 rx127byte_pkts; + /** Receive Size 128-255 Bytes Packet Count. */ + u32 rx255byte_pkts; + /** Receive Size 256-511 Bytes Packet Count. */ + u32 rx511byte_pkts; + /** Receive Size 512-1023 Bytes Packet Count. */ + u32 rx1023byte_pkts; + /** Receive Size 1024-1522 Bytes (or more, if configured) Packet Count. */ + u32 rx_max_byte_pkts; + /** Overall Transmit Good Packets Count. */ + u32 tx_good_pkts; + /** Transmit Unicast Packet Count. */ + u32 tx_unicast_pkts; + /** Transmit Broadcast Packet Count. */ + u32 tx_broadcast_pkts; + /** Transmit Multicast Packet Count. */ + u32 tx_multicast_pkts; + /** Transmit Single Collision Count. */ + u32 tx_single_coll_count; + /** Transmit Multiple Collision Count. */ + u32 tx_mult_coll_count; + /** Transmit Late Collision Count. */ + u32 tx_late_coll_count; + /** Transmit Excessive Collision Count. */ + u32 tx_excess_coll_count; + /** Transmit Collision Count. */ + u32 tx_coll_count; + /** Transmit Pause Packet Count. */ + u32 tx_pause_count; + /** Transmit Size 64 Bytes Packet Count. */ + u32 tx64byte_pkts; + /** Transmit Size 65-127 Bytes Packet Count. */ + u32 tx127byte_pkts; + /** Transmit Size 128-255 Bytes Packet Count. */ + u32 tx255byte_pkts; + /** Transmit Size 256-511 Bytes Packet Count. */ + u32 tx511byte_pkts; + /** Transmit Size 512-1023 Bytes Packet Count. */ + u32 tx1023byte_pkts; + /** Transmit Size 1024-1522 Bytes (or more, if configured) Packet Count. */ + u32 tx_max_byte_pkts; + /** Transmit Drop Packet Count. */ + u32 tx_dropped_pkts; + /** Transmit Dropped Packet Count, based on Congestion Management. */ + u32 tx_acm_dropped_pkts; + /** Receive Dropped Packet Count. */ + u32 rx_dropped_pkts; + /** Receive Good Byte Count (64 bit). */ + u64 rx_good_bytes; + /** Receive Bad Byte Count (64 bit). */ + u64 rx_bad_bytes; + /** Transmit Good Byte Count (64 bit). */ + u64 tx_good_bytes; + /**For GSWIP V32 only **/ + /** Receive Unicast Packet Count for Yellow & Red packet. */ + u32 rx_unicast_pkts_yellow_red; + /** Receive Broadcast Packet Count for Yellow & Red packet. */ + u32 rx_broadcast_pkts_yellow_red; + /** Receive Multicast Packet Count for Yellow & Red packet. */ + u32 rx_multicast_pkts_yellow_red; + /** Receive Good Byte Count (64 bit) for Yellow & Red packet. */ + u64 rx_good_bytes_yellow_red; + /** Receive Packet Count for Yellow & Red packet. */ + u32 rx_good_pkts_yellow_red; + /** Transmit Unicast Packet Count for Yellow & Red packet. */ + u32 tx_unicast_pkts_yellow_red; + /** Transmit Broadcast Packet Count for Yellow & Red packet. */ + u32 tx_broadcast_pkts_yellow_red; + /** Transmit Multicast Packet Count for Yellow & Red packet. */ + u32 tx_multicast_pkts_yellow_red; + /** Transmit Good Byte Count (64 bit) for Yellow & Red packet. */ + u64 tx_good_bytes_yellow_red; + /** Transmit Packet Count for Yellow & Red packet. */ + u32 tx_good_pkts_yellow_red; +} mxl862xx_debug_rmon_port_cnt_t; + +#pragma scalar_storage_order default +#pragma pack(pop) + +#endif /* _MXL862XX_RMON_H_ */ diff --git a/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_types.h b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_types.h new file mode 100644 index 000000000000..e65906c0a8e9 --- /dev/null +++ b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/host_api/mxl862xx_types.h @@ -0,0 +1,76 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * drivers/net/dsa/host_api/mxl862xx_types.h - DSA Driver for MaxLinear Mxl862xx switch chips family + * + * Copyright (C) 2024 MaxLinear Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef _MXL862XX_TYPES_H_ +#define _MXL862XX_TYPES_H_ + +#include + +/** \brief This is the unsigned 64-bit datatype. */ +typedef uint64_t u64; +/** \brief This is the unsigned 32-bit datatype. */ +typedef uint32_t u32; +/** \brief This is the unsigned 16-bit datatype. */ +typedef uint16_t u16; +/** \brief This is the unsigned 8-bit datatype. */ +typedef uint8_t u8; +/** \brief This is the signed 64-bit datatype. */ +typedef int64_t i64; +/** \brief This is the signed 32-bit datatype. */ +typedef int32_t i32; +/** \brief This is the signed 16-bit datatype. */ +typedef int16_t i16; +/** \brief This is the signed 8-bit datatype. */ +typedef int8_t i8; +/** \brief This is the signed 8-bit datatype. */ +typedef int32_t s32; +/** \brief This is the signed 8-bit datatype. */ +typedef int8_t s8; + + +/** \brief This is a union to describe the IPv4 and IPv6 Address in numeric representation. Used by multiple Structures and APIs. The member selection would be based upon \ref GSW_IP_Select_t */ +typedef union { + /** Describe the IPv4 address. + Only used if the IPv4 address should be read or configured. + Cannot be used together with the IPv6 address fields. */ + u32 nIPv4; + /** Describe the IPv6 address. + Only used if the IPv6 address should be read or configured. + Cannot be used together with the IPv4 address fields. */ + u16 nIPv6[8]; +} mxl862xx_ip_t; + +/** \brief Selection to use IPv4 or IPv6. + Used along with \ref GSW_IP_t to denote which union member to be accessed. +*/ +typedef enum { + /** IPv4 Type */ + MXL862XX_IP_SELECT_IPV4 = 0, + /** IPv6 Type */ + MXL862XX_IP_SELECT_IPV6 = 1 +} mxl862xx_ip_select_t; + +typedef struct { + int sw_addr; + struct mii_bus *bus; +} mxl862xx_device_t; +#endif /* _MXL862XX_TYPES_H_ */ diff --git a/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/mxl862xx.c b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/mxl862xx.c new file mode 100755 index 000000000000..7c4c9676a9ab --- /dev/null +++ b/target/linux/mediatek/files/drivers/net/dsa/mxl862xx/mxl862xx.c @@ -0,0 +1,4286 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * drivers/net/dsa/mxl862xx.c - DSA Driver for MaxLinear Mxl862xx switch chips family + * + * Copyright (C) 2024 MaxLinear Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 +#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"); diff --git a/target/linux/mediatek/files/net/dsa/tag_mxl862xx.c b/target/linux/mediatek/files/net/dsa/tag_mxl862xx.c new file mode 100644 index 000000000000..470c70853bc6 --- /dev/null +++ b/target/linux/mediatek/files/net/dsa/tag_mxl862xx.c @@ -0,0 +1,207 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * net/dsa/tag_mxl862xx.c - DSA driver Special Tag support for MaxLinear 862xx switch chips + * + * Copyright (C) 2024 MaxLinear Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#include +#include +#include +#include + +#ifndef LINUX_VERSION_CODE +#include +#else +#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c)) +#endif + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 2, 0)) +#include "dsa_priv.h" +#else +#include "tag.h" +#endif + +#define MXL862_NAME "mxl862xx" + +/* To define the outgoing port and to discover the incoming port a special + * tag is used by the GSW1xx. + * + * Dest MAC Src MAC special TAG EtherType + * ...| 1 2 3 4 5 6 | 1 2 3 4 5 6 | 1 2 3 4 5 6 7 8 | 1 2 |... + * |<--------------->| + */ + +/* special tag in TX path header */ +#define MXL862_TX_HEADER_LEN 8 + +#define MXL862_RX_HEADER_LEN 8 + +/* Byte 7 */ +#define MXL862_IGP_EGP_SHIFT 0 +#define MXL862_IGP_EGP_MASK GENMASK(3, 0) + +static struct sk_buff *mxl862_tag_xmit(struct sk_buff *skb, + struct net_device *dev) +{ +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) + int err; +#endif +#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0)) + struct dsa_port *dp = dsa_slave_to_port(dev); +#else + struct dsa_port *dp = dsa_user_to_port(dev); +#endif + struct dsa_port *cpu_dp = dp->cpu_dp; + unsigned int cpu_port = cpu_dp->index + 1; + unsigned int usr_port = dp->index + 1; + + u8 *mxl862_tag; + + if (skb == NULL) + return skb; + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 10, 0)) + err = skb_cow_head(skb, MXL862_TX_HEADER_LEN); + if (err) + return NULL; +#endif + + /* provide additional space 'MXL862_TX_HEADER_LEN' bytes */ + skb_push(skb, MXL862_TX_HEADER_LEN); + + /* shift MAC address to the beginnig of the enlarged buffer, + * releasing the space required for DSA tag (between MAC address and Ethertype) */ + memmove(skb->data, skb->data + MXL862_TX_HEADER_LEN, 2 * ETH_ALEN); + + /* special tag ingress */ + mxl862_tag = skb->data + 2 * ETH_ALEN; + mxl862_tag[0] = 0x88; + mxl862_tag[1] = 0xc3; + mxl862_tag[2] = 0; + mxl862_tag[3] = 0; + mxl862_tag[4] = 0; + mxl862_tag[5] = usr_port + 16 - cpu_port; + mxl862_tag[6] = 0; + mxl862_tag[7] = (cpu_port)&MXL862_IGP_EGP_MASK; + + return skb; +} + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 15, 0)) +static struct sk_buff *mxl862_tag_rcv(struct sk_buff *skb, + struct net_device *dev, + struct packet_type *pt) +#else +static struct sk_buff *mxl862_tag_rcv(struct sk_buff *skb, + struct net_device *dev) +#endif +{ + int port; + u8 *mxl862_tag; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 2, 0)) + struct dsa_port *dp; +#endif + + if (unlikely(!pskb_may_pull(skb, MXL862_RX_HEADER_LEN))) { + dev_warn_ratelimited(&dev->dev, + "Dropping packet, cannot pull SKB\n"); + return NULL; + } + + mxl862_tag = skb->data - 2; + + if ((mxl862_tag[0] != 0x88) && (mxl862_tag[1] != 0xc3)) { + dev_warn_ratelimited( + &dev->dev, + "Dropping packet due to invalid special tag marker\n"); + dev_warn_ratelimited( + &dev->dev, + "Rx Packet Tag: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n", + mxl862_tag[0], mxl862_tag[1], mxl862_tag[2], + mxl862_tag[3], mxl862_tag[4], mxl862_tag[5], + mxl862_tag[6], mxl862_tag[7]); + return NULL; + } + + /* Get source port information */ + port = (mxl862_tag[7] & MXL862_IGP_EGP_MASK) >> MXL862_IGP_EGP_SHIFT; + port = port - 1; +#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 7, 0)) + skb->dev = dsa_master_find_slave(dev, 0, port); +#else + skb->dev = dsa_conduit_find_user(dev, 0, port); +#endif + if (!skb->dev) { + dev_warn_ratelimited( + &dev->dev, + "Dropping packet due to invalid source port\n"); + dev_warn_ratelimited( + &dev->dev, + "Rx Packet Tag: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n", + mxl862_tag[0], mxl862_tag[1], mxl862_tag[2], + mxl862_tag[3], mxl862_tag[4], mxl862_tag[5], + mxl862_tag[6], mxl862_tag[7]); + return NULL; + } + + /* remove the MxL862xx special tag between the MAC addresses and the current ethertype field. */ + skb_pull_rcsum(skb, MXL862_RX_HEADER_LEN); + memmove(skb->data - ETH_HLEN, + skb->data - (ETH_HLEN + MXL862_RX_HEADER_LEN), 2 * ETH_ALEN); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 2, 0)) + dp = dsa_slave_to_port(skb->dev); + if (dp->bridge_dev) + skb->offload_fwd_mark = 1; +#else + dsa_default_offload_fwd_mark(skb); +#endif + + return skb; +} + +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 2, 0)) +const struct dsa_device_ops mxl862_netdev_ops = { + .xmit = mxl862_tag_xmit, + .rcv = mxl862_tag_rcv, +}; +#else + +static const struct dsa_device_ops mxl862_netdev_ops = { + .name = "mxl862", + .proto = DSA_TAG_PROTO_MXL862, + .xmit = mxl862_tag_xmit, + .rcv = mxl862_tag_rcv, +#if (LINUX_VERSION_CODE < KERNEL_VERSION(5, 14, 0)) + .overhead = MXL862_RX_HEADER_LEN, +#else + .needed_headroom = MXL862_RX_HEADER_LEN, +#endif +}; + +MODULE_LICENSE("GPL"); +#if (LINUX_VERSION_CODE < KERNEL_VERSION(6, 2, 0)) +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_MXL862); +#else +MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_MXL862, MXL862_NAME); +#endif + +module_dsa_tag_driver(mxl862_netdev_ops); +#endif + +MODULE_LICENSE("GPL"); diff --git a/target/linux/mediatek/files/net/dsa/tag_mxl862xx_8021q.c b/target/linux/mediatek/files/net/dsa/tag_mxl862xx_8021q.c new file mode 100644 index 000000000000..96e911df29ce --- /dev/null +++ b/target/linux/mediatek/files/net/dsa/tag_mxl862xx_8021q.c @@ -0,0 +1,191 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * net/dsa/tag_mxl862xx_8021q.c - DSA driver 802.1q based Special Tag support for MaxLinear 862xx switch chips + * + * Copyright (C) 2024 MaxLinear Inc. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + +#ifndef LINUX_VERSION_CODE +#include +#else +#define KERNEL_VERSION(a, b, c) (((a) << 16) + ((b) << 8) + (c)) +#endif + +#include +#if (LINUX_VERSION_CODE > KERNEL_VERSION(6, 1, 0)) +#include "tag_8021q.h" +#endif +#include + +#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); diff --git a/target/linux/mediatek/filogic/base-files/etc/board.d/02_network b/target/linux/mediatek/filogic/base-files/etc/board.d/02_network index e9f63938b2d3..8ef8c66a11e7 100644 --- a/target/linux/mediatek/filogic/base-files/etc/board.d/02_network +++ b/target/linux/mediatek/filogic/base-files/etc/board.d/02_network @@ -75,6 +75,9 @@ mediatek_setup_interfaces() comfast,cf-e393ax) ucidef_set_interfaces_lan_wan "lan1" eth1 ;; + openwrt,two) + ucidef_set_interface_lan "mxl1 mxl2 mxl3 mxl4" + ;; cudy,ap3000outdoor-v1|\ cudy,ap3000-v1|\ cudy,re3000-v1|\ diff --git a/target/linux/mediatek/filogic/config-6.6 b/target/linux/mediatek/filogic/config-6.6 index fe85b9d3f141..d7bf17833285 100644 --- a/target/linux/mediatek/filogic/config-6.6 +++ b/target/linux/mediatek/filogic/config-6.6 @@ -296,10 +296,13 @@ CONFIG_NEED_SG_DMA_LENGTH=y CONFIG_NET_DEVLINK=y CONFIG_NET_DSA=y CONFIG_NET_DSA_AN8855=y +CONFIG_NET_DSA_MXL862=y CONFIG_NET_DSA_MT7530=y CONFIG_NET_DSA_MT7530_MDIO=y CONFIG_NET_DSA_MT7530_MMIO=y CONFIG_NET_DSA_TAG_MTK=y +CONFIG_NET_DSA_TAG_MXL862=y +CONFIG_NET_DSA_TAG_MXL862_8021Q=y CONFIG_NET_EGRESS=y CONFIG_NET_FLOW_LIMIT=y CONFIG_NET_INGRESS=y diff --git a/target/linux/mediatek/image/filogic.mk b/target/linux/mediatek/image/filogic.mk index b188add1bd3e..187cbaef0436 100644 --- a/target/linux/mediatek/image/filogic.mk +++ b/target/linux/mediatek/image/filogic.mk @@ -1324,6 +1324,21 @@ define Device/openwrt_one endef TARGET_DEVICES += openwrt_one +define Device/openwrt_two + DEVICE_VENDOR := OpenWrt + DEVICE_MODEL := Two + DEVICE_DTS := mt7988a-openwrt-two + DEVICE_DTS_DIR := ../dts + DEVICE_DTC_FLAGS := --pad 4096 + DEVICE_DTS_LOADADDR := 0x45f00000 + KERNEL_LOADADDR := 0x46000000 + KERNEL := kernel-bin | gzip + KERNEL_INITRAMFS := kernel-bin | lzma | \ + fit lzma $$(KDIR)/image-$$(firstword $$(DEVICE_DTS)).dtb with-initrd | pad-to 64k + KERNEL_INITRAMFS_SUFFIX := .itb +endef +TARGET_DEVICES += openwrt_two + define Device/qihoo_360t7 DEVICE_VENDOR := Qihoo DEVICE_MODEL := 360T7 diff --git a/target/linux/mediatek/patches-6.6/990-50-smartrg-net-dsa-mxl862xx-add-driver.patch b/target/linux/mediatek/patches-6.6/990-50-smartrg-net-dsa-mxl862xx-add-driver.patch new file mode 100644 index 000000000000..59c80c97db96 --- /dev/null +++ b/target/linux/mediatek/patches-6.6/990-50-smartrg-net-dsa-mxl862xx-add-driver.patch @@ -0,0 +1,89 @@ +From: Chad Monroe +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 +--- + drivers/net/dsa/Kconfig | 2 ++ + drivers/net/dsa/Makefile | 1 + + include/net/dsa.h | 4 ++++ + net/dsa/Kconfig | 13 +++++++++++++ + net/dsa/Makefile | 2 ++ + 5 files changed, 22 insertions(+) + +--- a/drivers/net/dsa/Kconfig ++++ b/drivers/net/dsa/Kconfig +@@ -83,6 +83,8 @@ source "drivers/net/dsa/ocelot/Kconfig" + + source "drivers/net/dsa/qca/Kconfig" + ++source "drivers/net/dsa/mxl862xx/Kconfig" ++ + source "drivers/net/dsa/sja1105/Kconfig" + + source "drivers/net/dsa/xrs700x/Kconfig" +--- a/drivers/net/dsa/Makefile ++++ b/drivers/net/dsa/Makefile +@@ -21,6 +21,7 @@ obj-y += b53/ + obj-y += hirschmann/ + obj-y += microchip/ + obj-y += mv88e6xxx/ ++obj-y += mxl862xx/ + obj-y += ocelot/ + obj-y += qca/ + obj-y += realtek/ +--- a/include/net/dsa.h ++++ b/include/net/dsa.h +@@ -56,6 +56,8 @@ struct phylink_link_state; + #define DSA_TAG_PROTO_RTL8_4T_VALUE 25 + #define DSA_TAG_PROTO_RZN1_A5PSW_VALUE 26 + #define DSA_TAG_PROTO_LAN937X_VALUE 27 ++#define DSA_TAG_PROTO_MXL862_VALUE 28 ++#define DSA_TAG_PROTO_MXL862_8021Q_VALUE 29 + + enum dsa_tag_protocol { + DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE, +@@ -86,6 +88,8 @@ enum dsa_tag_protocol { + DSA_TAG_PROTO_RTL8_4T = DSA_TAG_PROTO_RTL8_4T_VALUE, + DSA_TAG_PROTO_RZN1_A5PSW = DSA_TAG_PROTO_RZN1_A5PSW_VALUE, + DSA_TAG_PROTO_LAN937X = DSA_TAG_PROTO_LAN937X_VALUE, ++ DSA_TAG_PROTO_MXL862 = DSA_TAG_PROTO_MXL862_VALUE, ++ DSA_TAG_PROTO_MXL862_8021Q = DSA_TAG_PROTO_MXL862_8021Q_VALUE, + }; + + struct dsa_switch; +--- a/net/dsa/Kconfig ++++ b/net/dsa/Kconfig +@@ -125,6 +125,19 @@ config NET_DSA_TAG_QCA + Say Y or M if you want to enable support for tagging frames for + the Qualcomm Atheros QCA8K switches. + ++config NET_DSA_TAG_MXL862 ++ tristate "Tag driver for MxL862xx switches" ++ help ++ Say Y or M if you want to enable support for tagging frames for the ++ Maxlinear MxL862xx switches. ++ ++config NET_DSA_TAG_MXL862_8021Q ++ tristate "Tag driver for MxL862xx switches, based on VLAN tags" ++ help ++ Say Y or M if you want to enable support for tagging frames for the ++ Maxlinear MxL862xx switches. This tagging variant is based on 4-byte wide VLAN ++ tags ++ + config NET_DSA_TAG_RTL4_A + tristate "Tag driver for Realtek 4 byte protocol A tags" + help +--- a/net/dsa/Makefile ++++ b/net/dsa/Makefile +@@ -28,6 +28,8 @@ obj-$(CONFIG_NET_DSA_TAG_HELLCREEK) += t + obj-$(CONFIG_NET_DSA_TAG_KSZ) += tag_ksz.o + obj-$(CONFIG_NET_DSA_TAG_LAN9303) += tag_lan9303.o + obj-$(CONFIG_NET_DSA_TAG_MTK) += tag_mtk.o ++obj-$(CONFIG_NET_DSA_TAG_MXL862) += tag_mxl862xx.o ++obj-$(CONFIG_NET_DSA_TAG_MXL862_8021Q) += tag_mxl862xx_8021q.o + obj-$(CONFIG_NET_DSA_TAG_NONE) += tag_none.o + obj-$(CONFIG_NET_DSA_TAG_OCELOT) += tag_ocelot.o + obj-$(CONFIG_NET_DSA_TAG_OCELOT_8021Q) += tag_ocelot_8021q.o diff --git a/target/linux/mediatek/patches-6.6/990-51-smartrg-net-phy-mxl-gpy-add-mxl862xx-support.patch b/target/linux/mediatek/patches-6.6/990-51-smartrg-net-phy-mxl-gpy-add-mxl862xx-support.patch new file mode 100644 index 000000000000..8cfed15f5a90 --- /dev/null +++ b/target/linux/mediatek/patches-6.6/990-51-smartrg-net-phy-mxl-gpy-add-mxl862xx-support.patch @@ -0,0 +1,76 @@ +From: Chad Monroe +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 +--- + 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);