Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'net-dsa-initial-support-for-maxlinear-mxl862xx-switches'

Daniel Golle says:

====================
net: dsa: initial support for MaxLinear MxL862xx switches

This series adds very basic DSA support for the MaxLinear MxL86252
(5x 2500Base-T PHYs) and MxL86282 (8x 2500Base-T PHYs) switches.
In addition to the 2.5G TP ports both switches also come with two
SerDes interfaces which can be used either to connect external PHYs
or SFP cages, or as CPU port when using the switch with this DSA driver.

MxL862xx integrates a firmware running on an embedded processor (based on
Zephyr RTOS). Host interaction uses a simple netlink-like API transported
over MDIO/MMD.

This series includes only what's needed to pass traffic between user
ports and the CPU port: relayed MDIO to internal PHYs, basic port
enable/disable, and CPU-port special tagging.

The SerDes interface of the CPU port is automatically configured by the
switch after reset using a board-specific configuration stored together
with the firmware in the flash chip attached to the switch, so no action
is needed from the driver to setup the interface mode of the CPU port.

Also MAC settings of the PHY ports are automatically configured, which
means the driver works fine with phylink_mac_ops being all no-op stubs.

Multiple follow up series will bring support for setting up the other
SerDes PCS interface (ie. not used for the CPU port), bridge, VLAN, ...
offloading, and support for using an 802.1Q-based special tag instead of
the proprietary 8-byte tag.
====================

Link: https://patch.msgid.link/cover.1770433307.git.daniel@makrotopia.org
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

+1793
+161
Documentation/devicetree/bindings/net/dsa/maxlinear,mxl862xx.yaml
··· 1 + # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) 2 + %YAML 1.2 3 + --- 4 + $id: http://devicetree.org/schemas/net/dsa/maxlinear,mxl862xx.yaml# 5 + $schema: http://devicetree.org/meta-schemas/core.yaml# 6 + 7 + title: MaxLinear MxL862xx Ethernet Switch Family 8 + 9 + maintainers: 10 + - Daniel Golle <daniel@makrotopia.org> 11 + 12 + description: 13 + The MaxLinear MxL862xx switch family are multi-port Ethernet switches with 14 + integrated 2.5GE PHYs. The MxL86252 has five PHY ports and the MxL86282 15 + has eight PHY ports. Both models come with two 10 Gigabit/s SerDes 16 + interfaces to be used to connect external PHYs or SFP cages, or as CPU 17 + port. 18 + 19 + allOf: 20 + - $ref: dsa.yaml#/$defs/ethernet-ports 21 + 22 + properties: 23 + compatible: 24 + enum: 25 + - maxlinear,mxl86252 26 + - maxlinear,mxl86282 27 + 28 + reg: 29 + maxItems: 1 30 + description: MDIO address of the switch 31 + 32 + mdio: 33 + $ref: /schemas/net/mdio.yaml# 34 + unevaluatedProperties: false 35 + 36 + required: 37 + - compatible 38 + - mdio 39 + - reg 40 + 41 + unevaluatedProperties: false 42 + 43 + examples: 44 + - | 45 + mdio { 46 + #address-cells = <1>; 47 + #size-cells = <0>; 48 + 49 + switch@0 { 50 + compatible = "maxlinear,mxl86282"; 51 + reg = <0>; 52 + 53 + ethernet-ports { 54 + #address-cells = <1>; 55 + #size-cells = <0>; 56 + 57 + /* Microcontroller port */ 58 + port@0 { 59 + reg = <0>; 60 + status = "disabled"; 61 + }; 62 + 63 + port@1 { 64 + reg = <1>; 65 + phy-handle = <&phy0>; 66 + phy-mode = "internal"; 67 + }; 68 + 69 + port@2 { 70 + reg = <2>; 71 + phy-handle = <&phy1>; 72 + phy-mode = "internal"; 73 + }; 74 + 75 + port@3 { 76 + reg = <3>; 77 + phy-handle = <&phy2>; 78 + phy-mode = "internal"; 79 + }; 80 + 81 + port@4 { 82 + reg = <4>; 83 + phy-handle = <&phy3>; 84 + phy-mode = "internal"; 85 + }; 86 + 87 + port@5 { 88 + reg = <5>; 89 + phy-handle = <&phy4>; 90 + phy-mode = "internal"; 91 + }; 92 + 93 + port@6 { 94 + reg = <6>; 95 + phy-handle = <&phy5>; 96 + phy-mode = "internal"; 97 + }; 98 + 99 + port@7 { 100 + reg = <7>; 101 + phy-handle = <&phy6>; 102 + phy-mode = "internal"; 103 + }; 104 + 105 + port@8 { 106 + reg = <8>; 107 + phy-handle = <&phy7>; 108 + phy-mode = "internal"; 109 + }; 110 + 111 + port@9 { 112 + reg = <9>; 113 + label = "cpu"; 114 + ethernet = <&gmac0>; 115 + phy-mode = "usxgmii"; 116 + 117 + fixed-link { 118 + speed = <10000>; 119 + full-duplex; 120 + }; 121 + }; 122 + }; 123 + 124 + mdio { 125 + #address-cells = <1>; 126 + #size-cells = <0>; 127 + 128 + phy0: ethernet-phy@0 { 129 + reg = <0>; 130 + }; 131 + 132 + phy1: ethernet-phy@1 { 133 + reg = <1>; 134 + }; 135 + 136 + phy2: ethernet-phy@2 { 137 + reg = <2>; 138 + }; 139 + 140 + phy3: ethernet-phy@3 { 141 + reg = <3>; 142 + }; 143 + 144 + phy4: ethernet-phy@4 { 145 + reg = <4>; 146 + }; 147 + 148 + phy5: ethernet-phy@5 { 149 + reg = <5>; 150 + }; 151 + 152 + phy6: ethernet-phy@6 { 153 + reg = <6>; 154 + }; 155 + 156 + phy7: ethernet-phy@7 { 157 + reg = <7>; 158 + }; 159 + }; 160 + }; 161 + };
+8
MAINTAINERS
··· 15626 15626 F: drivers/net/phy/mxl-86110.c 15627 15627 F: drivers/net/phy/mxl-gpy.c 15628 15628 15629 + MAXLINEAR MXL862XX SWITCH DRIVER 15630 + M: Daniel Golle <daniel@makrotopia.org> 15631 + L: netdev@vger.kernel.org 15632 + S: Maintained 15633 + F: Documentation/devicetree/bindings/net/dsa/maxlinear,mxl862xx.yaml 15634 + F: drivers/net/dsa/mxl862xx/ 15635 + F: net/dsa/tag_mxl862xx.c 15636 + 15629 15637 MCAN DEVICE DRIVER 15630 15638 M: Markus Schneider-Pargmann <msp@baylibre.com> 15631 15639 L: linux-can@vger.kernel.org
+2
drivers/net/dsa/Kconfig
··· 74 74 75 75 source "drivers/net/dsa/mv88e6xxx/Kconfig" 76 76 77 + source "drivers/net/dsa/mxl862xx/Kconfig" 78 + 77 79 source "drivers/net/dsa/ocelot/Kconfig" 78 80 79 81 source "drivers/net/dsa/qca/Kconfig"
+1
drivers/net/dsa/Makefile
··· 20 20 obj-y += lantiq/ 21 21 obj-y += microchip/ 22 22 obj-y += mv88e6xxx/ 23 + obj-y += mxl862xx/ 23 24 obj-y += ocelot/ 24 25 obj-y += qca/ 25 26 obj-y += realtek/
+12
drivers/net/dsa/mxl862xx/Kconfig
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + config NET_DSA_MXL862 3 + tristate "MaxLinear MxL862xx" 4 + depends on NET_DSA 5 + select MAXLINEAR_GPHY 6 + select NET_DSA_TAG_MXL_862XX 7 + help 8 + This enables support for the MaxLinear MxL862xx switch family. 9 + These switches have two 10GE SerDes interfaces, one typically 10 + used as CPU port. 11 + - MxL86282 has eight 2.5 Gigabit PHYs 12 + - MxL86252 has five 2.5 Gigabit PHYs
+3
drivers/net/dsa/mxl862xx/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + obj-$(CONFIG_NET_DSA_MXL862) += mxl862xx_dsa.o 3 + mxl862xx_dsa-y := mxl862xx.o mxl862xx-host.o
+675
drivers/net/dsa/mxl862xx/mxl862xx-api.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + 3 + #ifndef __MXL862XX_API_H 4 + #define __MXL862XX_API_H 5 + 6 + #include <linux/if_ether.h> 7 + 8 + /** 9 + * struct mdio_relay_data - relayed access to the switch internal MDIO bus 10 + * @data: data to be read or written 11 + * @phy: PHY index 12 + * @mmd: MMD device 13 + * @reg: register index 14 + */ 15 + struct mdio_relay_data { 16 + __le16 data; 17 + u8 phy; 18 + u8 mmd; 19 + __le16 reg; 20 + } __packed; 21 + 22 + /** 23 + * struct mxl862xx_register_mod - Register access parameter to directly 24 + * modify internal registers 25 + * @addr: Register address offset for modification 26 + * @data: Value to write to the register address 27 + * @mask: Mask of bits to be modified (1 to modify, 0 to ignore) 28 + * 29 + * Used for direct register modification operations. 30 + */ 31 + struct mxl862xx_register_mod { 32 + __le16 addr; 33 + __le16 data; 34 + __le16 mask; 35 + } __packed; 36 + 37 + /** 38 + * enum mxl862xx_mac_clear_type - MAC table clear type 39 + * @MXL862XX_MAC_CLEAR_PHY_PORT: clear dynamic entries based on port_id 40 + * @MXL862XX_MAC_CLEAR_DYNAMIC: clear all dynamic entries 41 + */ 42 + enum mxl862xx_mac_clear_type { 43 + MXL862XX_MAC_CLEAR_PHY_PORT = 0, 44 + MXL862XX_MAC_CLEAR_DYNAMIC, 45 + }; 46 + 47 + /** 48 + * struct mxl862xx_mac_table_clear - MAC table clear 49 + * @type: see &enum mxl862xx_mac_clear_type 50 + * @port_id: physical port id 51 + */ 52 + struct mxl862xx_mac_table_clear { 53 + u8 type; 54 + u8 port_id; 55 + } __packed; 56 + 57 + /** 58 + * enum mxl862xx_age_timer - Aging Timer Value. 59 + * @MXL862XX_AGETIMER_1_SEC: 1 second aging time 60 + * @MXL862XX_AGETIMER_10_SEC: 10 seconds aging time 61 + * @MXL862XX_AGETIMER_300_SEC: 300 seconds aging time 62 + * @MXL862XX_AGETIMER_1_HOUR: 1 hour aging time 63 + * @MXL862XX_AGETIMER_1_DAY: 24 hours aging time 64 + * @MXL862XX_AGETIMER_CUSTOM: Custom aging time in seconds 65 + */ 66 + enum mxl862xx_age_timer { 67 + MXL862XX_AGETIMER_1_SEC = 1, 68 + MXL862XX_AGETIMER_10_SEC, 69 + MXL862XX_AGETIMER_300_SEC, 70 + MXL862XX_AGETIMER_1_HOUR, 71 + MXL862XX_AGETIMER_1_DAY, 72 + MXL862XX_AGETIMER_CUSTOM, 73 + }; 74 + 75 + /** 76 + * struct mxl862xx_bridge_alloc - Bridge Allocation 77 + * @bridge_id: If the bridge allocation is successful, a valid ID will be 78 + * returned in this field. Otherwise, INVALID_HANDLE is 79 + * returned. For bridge free, this field should contain a 80 + * valid ID returned by the bridge allocation. ID 0 is not 81 + * used for historic reasons. 82 + * 83 + * Used by MXL862XX_BRIDGE_ALLOC and MXL862XX_BRIDGE_FREE. 84 + */ 85 + struct mxl862xx_bridge_alloc { 86 + __le16 bridge_id; 87 + }; 88 + 89 + /** 90 + * enum mxl862xx_bridge_config_mask - Bridge configuration mask 91 + * @MXL862XX_BRIDGE_CONFIG_MASK_MAC_LEARNING_LIMIT: 92 + * Mask for mac_learning_limit_enable and mac_learning_limit. 93 + * @MXL862XX_BRIDGE_CONFIG_MASK_MAC_LEARNED_COUNT: 94 + * Mask for mac_learning_count 95 + * @MXL862XX_BRIDGE_CONFIG_MASK_MAC_DISCARD_COUNT: 96 + * Mask for learning_discard_event 97 + * @MXL862XX_BRIDGE_CONFIG_MASK_SUB_METER: 98 + * Mask for sub_metering_enable and traffic_sub_meter_id 99 + * @MXL862XX_BRIDGE_CONFIG_MASK_FORWARDING_MODE: 100 + * Mask for forward_broadcast, forward_unknown_multicast_ip, 101 + * forward_unknown_multicast_non_ip and forward_unknown_unicast. 102 + * @MXL862XX_BRIDGE_CONFIG_MASK_ALL: Enable all 103 + * @MXL862XX_BRIDGE_CONFIG_MASK_FORCE: Bypass any check for debug purpose 104 + */ 105 + enum mxl862xx_bridge_config_mask { 106 + MXL862XX_BRIDGE_CONFIG_MASK_MAC_LEARNING_LIMIT = BIT(0), 107 + MXL862XX_BRIDGE_CONFIG_MASK_MAC_LEARNED_COUNT = BIT(1), 108 + MXL862XX_BRIDGE_CONFIG_MASK_MAC_DISCARD_COUNT = BIT(2), 109 + MXL862XX_BRIDGE_CONFIG_MASK_SUB_METER = BIT(3), 110 + MXL862XX_BRIDGE_CONFIG_MASK_FORWARDING_MODE = BIT(4), 111 + MXL862XX_BRIDGE_CONFIG_MASK_ALL = 0x7FFFFFFF, 112 + MXL862XX_BRIDGE_CONFIG_MASK_FORCE = BIT(31) 113 + }; 114 + 115 + /** 116 + * enum mxl862xx_bridge_port_egress_meter - Meters for egress traffic type 117 + * @MXL862XX_BRIDGE_PORT_EGRESS_METER_BROADCAST: 118 + * Index of broadcast traffic meter 119 + * @MXL862XX_BRIDGE_PORT_EGRESS_METER_MULTICAST: 120 + * Index of known multicast traffic meter 121 + * @MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_IP: 122 + * Index of unknown multicast IP traffic meter 123 + * @MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_NON_IP: 124 + * Index of unknown multicast non-IP traffic meter 125 + * @MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_UC: 126 + * Index of unknown unicast traffic meter 127 + * @MXL862XX_BRIDGE_PORT_EGRESS_METER_OTHERS: 128 + * Index of traffic meter for other types 129 + * @MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX: Number of index 130 + */ 131 + enum mxl862xx_bridge_port_egress_meter { 132 + MXL862XX_BRIDGE_PORT_EGRESS_METER_BROADCAST = 0, 133 + MXL862XX_BRIDGE_PORT_EGRESS_METER_MULTICAST, 134 + MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_IP, 135 + MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_NON_IP, 136 + MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_UC, 137 + MXL862XX_BRIDGE_PORT_EGRESS_METER_OTHERS, 138 + MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX, 139 + }; 140 + 141 + /** 142 + * enum mxl862xx_bridge_forward_mode - Bridge forwarding type of packet 143 + * @MXL862XX_BRIDGE_FORWARD_FLOOD: Packet is flooded to port members of 144 + * ingress bridge port 145 + * @MXL862XX_BRIDGE_FORWARD_DISCARD: Packet is discarded 146 + */ 147 + enum mxl862xx_bridge_forward_mode { 148 + MXL862XX_BRIDGE_FORWARD_FLOOD = 0, 149 + MXL862XX_BRIDGE_FORWARD_DISCARD, 150 + }; 151 + 152 + /** 153 + * struct mxl862xx_bridge_config - Bridge Configuration 154 + * @bridge_id: Bridge ID (FID) 155 + * @mask: See &enum mxl862xx_bridge_config_mask 156 + * @mac_learning_limit_enable: Enable MAC learning limitation 157 + * @mac_learning_limit: Max number of MAC addresses that can be learned in 158 + * this bridge (all bridge ports) 159 + * @mac_learning_count: Number of MAC addresses learned from this bridge 160 + * @learning_discard_event: Number of learning discard events due to 161 + * hardware resource not available 162 + * @sub_metering_enable: Traffic metering on type of traffic (such as 163 + * broadcast, multicast, unknown unicast, etc) applies 164 + * @traffic_sub_meter_id: Meter for bridge process with specific type (such 165 + * as broadcast, multicast, unknown unicast, etc) 166 + * @forward_broadcast: Forwarding mode of broadcast traffic. See 167 + * &enum mxl862xx_bridge_forward_mode 168 + * @forward_unknown_multicast_ip: Forwarding mode of unknown multicast IP 169 + * traffic. 170 + * See &enum mxl862xx_bridge_forward_mode 171 + * @forward_unknown_multicast_non_ip: Forwarding mode of unknown multicast 172 + * non-IP traffic. 173 + * See &enum mxl862xx_bridge_forward_mode 174 + * @forward_unknown_unicast: Forwarding mode of unknown unicast traffic. See 175 + * &enum mxl862xx_bridge_forward_mode 176 + */ 177 + struct mxl862xx_bridge_config { 178 + __le16 bridge_id; 179 + __le32 mask; /* enum mxl862xx_bridge_config_mask */ 180 + u8 mac_learning_limit_enable; 181 + __le16 mac_learning_limit; 182 + __le16 mac_learning_count; 183 + __le32 learning_discard_event; 184 + u8 sub_metering_enable[MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX]; 185 + __le16 traffic_sub_meter_id[MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX]; 186 + __le32 forward_broadcast; /* enum mxl862xx_bridge_forward_mode */ 187 + __le32 forward_unknown_multicast_ip; /* enum mxl862xx_bridge_forward_mode */ 188 + __le32 forward_unknown_multicast_non_ip; /* enum mxl862xx_bridge_forward_mode */ 189 + __le32 forward_unknown_unicast; /* enum mxl862xx_bridge_forward_mode */ 190 + } __packed; 191 + 192 + /** 193 + * struct mxl862xx_bridge_port_alloc - Bridge Port Allocation 194 + * @bridge_port_id: If the bridge port allocation is successful, a valid ID 195 + * will be returned in this field. Otherwise, INVALID_HANDLE 196 + * is returned. For bridge port free, this field should 197 + * contain a valid ID returned by the bridge port allocation. 198 + * 199 + * Used by MXL862XX_BRIDGE_PORT_ALLOC and MXL862XX_BRIDGE_PORT_FREE. 200 + */ 201 + struct mxl862xx_bridge_port_alloc { 202 + __le16 bridge_port_id; 203 + }; 204 + 205 + /** 206 + * enum mxl862xx_bridge_port_config_mask - Bridge Port configuration mask 207 + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID: 208 + * Mask for bridge_id 209 + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN: 210 + * Mask for ingress_extended_vlan_enable, 211 + * ingress_extended_vlan_block_id and ingress_extended_vlan_block_size 212 + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN: 213 + * Mask for egress_extended_vlan_enable, egress_extended_vlan_block_id 214 + * and egress_extended_vlan_block_size 215 + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_MARKING: 216 + * Mask for ingress_marking_mode 217 + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_REMARKING: 218 + * Mask for egress_remarking_mode 219 + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_METER: 220 + * Mask for ingress_metering_enable and ingress_traffic_meter_id 221 + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_SUB_METER: 222 + * Mask for egress_sub_metering_enable and egress_traffic_sub_meter_id 223 + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_CTP_MAPPING: 224 + * Mask for dest_logical_port_id, pmapper_enable, dest_sub_if_id_group, 225 + * pmapper_mapping_mode, pmapper_id_valid and pmapper 226 + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP: 227 + * Mask for bridge_port_map 228 + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_DEST_IP_LOOKUP: 229 + * Mask for mc_dest_ip_lookup_disable 230 + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_IP_LOOKUP: 231 + * Mask for mc_src_ip_lookup_enable 232 + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_DEST_MAC_LOOKUP: 233 + * Mask for dest_mac_lookup_disable 234 + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING: 235 + * Mask for src_mac_learning_disable 236 + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_MAC_SPOOFING: 237 + * Mask for mac_spoofing_detect_enable 238 + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_PORT_LOCK: 239 + * Mask for port_lock_enable 240 + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_MAC_LEARNING_LIMIT: 241 + * Mask for mac_learning_limit_enable and mac_learning_limit 242 + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_MAC_LEARNED_COUNT: 243 + * Mask for mac_learning_count 244 + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN_FILTER: 245 + * Mask for ingress_vlan_filter_enable, ingress_vlan_filter_block_id 246 + * and ingress_vlan_filter_block_size 247 + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN_FILTER1: 248 + * Mask for bypass_egress_vlan_filter1, egress_vlan_filter1enable, 249 + * egress_vlan_filter1block_id and egress_vlan_filter1block_size 250 + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN_FILTER2: 251 + * Mask for egress_vlan_filter2enable, egress_vlan_filter2block_id and 252 + * egress_vlan_filter2block_size 253 + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MAC_LEARNING: 254 + * Mask for vlan_tag_selection, vlan_src_mac_priority_enable, 255 + * vlan_src_mac_dei_enable, vlan_src_mac_vid_enable, 256 + * vlan_dst_mac_priority_enable, vlan_dst_mac_dei_enable and 257 + * vlan_dst_mac_vid_enable 258 + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MULTICAST_LOOKUP: 259 + * Mask for vlan_multicast_priority_enable, 260 + * vlan_multicast_dei_enable and vlan_multicast_vid_enable 261 + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_LOOP_VIOLATION_COUNTER: 262 + * Mask for loop_violation_count 263 + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_ALL: Enable all 264 + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_FORCE: Bypass any check for debug purpose 265 + */ 266 + enum mxl862xx_bridge_port_config_mask { 267 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID = BIT(0), 268 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN = BIT(1), 269 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN = BIT(2), 270 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_MARKING = BIT(3), 271 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_REMARKING = BIT(4), 272 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_METER = BIT(5), 273 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_SUB_METER = BIT(6), 274 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_CTP_MAPPING = BIT(7), 275 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP = BIT(8), 276 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_DEST_IP_LOOKUP = BIT(9), 277 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_IP_LOOKUP = BIT(10), 278 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_DEST_MAC_LOOKUP = BIT(11), 279 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING = BIT(12), 280 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_MAC_SPOOFING = BIT(13), 281 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_PORT_LOCK = BIT(14), 282 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_MAC_LEARNING_LIMIT = BIT(15), 283 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_MAC_LEARNED_COUNT = BIT(16), 284 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN_FILTER = BIT(17), 285 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN_FILTER1 = BIT(18), 286 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN_FILTER2 = BIT(19), 287 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MAC_LEARNING = BIT(20), 288 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MULTICAST_LOOKUP = BIT(21), 289 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_LOOP_VIOLATION_COUNTER = BIT(22), 290 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_ALL = 0x7FFFFFFF, 291 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_FORCE = BIT(31) 292 + }; 293 + 294 + /** 295 + * enum mxl862xx_color_marking_mode - Color Marking Mode 296 + * @MXL862XX_MARKING_ALL_GREEN: mark packets (except critical) to green 297 + * @MXL862XX_MARKING_INTERNAL_MARKING: do not change color and priority 298 + * @MXL862XX_MARKING_DEI: DEI mark mode 299 + * @MXL862XX_MARKING_PCP_8P0D: PCP 8P0D mark mode 300 + * @MXL862XX_MARKING_PCP_7P1D: PCP 7P1D mark mode 301 + * @MXL862XX_MARKING_PCP_6P2D: PCP 6P2D mark mode 302 + * @MXL862XX_MARKING_PCP_5P3D: PCP 5P3D mark mode 303 + * @MXL862XX_MARKING_DSCP_AF: DSCP AF class 304 + */ 305 + enum mxl862xx_color_marking_mode { 306 + MXL862XX_MARKING_ALL_GREEN = 0, 307 + MXL862XX_MARKING_INTERNAL_MARKING, 308 + MXL862XX_MARKING_DEI, 309 + MXL862XX_MARKING_PCP_8P0D, 310 + MXL862XX_MARKING_PCP_7P1D, 311 + MXL862XX_MARKING_PCP_6P2D, 312 + MXL862XX_MARKING_PCP_5P3D, 313 + MXL862XX_MARKING_DSCP_AF, 314 + }; 315 + 316 + /** 317 + * enum mxl862xx_color_remarking_mode - Color Remarking Mode 318 + * @MXL862XX_REMARKING_NONE: values from last process stage 319 + * @MXL862XX_REMARKING_DEI: DEI mark mode 320 + * @MXL862XX_REMARKING_PCP_8P0D: PCP 8P0D mark mode 321 + * @MXL862XX_REMARKING_PCP_7P1D: PCP 7P1D mark mode 322 + * @MXL862XX_REMARKING_PCP_6P2D: PCP 6P2D mark mode 323 + * @MXL862XX_REMARKING_PCP_5P3D: PCP 5P3D mark mode 324 + * @MXL862XX_REMARKING_DSCP_AF: DSCP AF class 325 + */ 326 + enum mxl862xx_color_remarking_mode { 327 + MXL862XX_REMARKING_NONE = 0, 328 + MXL862XX_REMARKING_DEI = 2, 329 + MXL862XX_REMARKING_PCP_8P0D, 330 + MXL862XX_REMARKING_PCP_7P1D, 331 + MXL862XX_REMARKING_PCP_6P2D, 332 + MXL862XX_REMARKING_PCP_5P3D, 333 + MXL862XX_REMARKING_DSCP_AF, 334 + }; 335 + 336 + /** 337 + * enum mxl862xx_pmapper_mapping_mode - P-mapper Mapping Mode 338 + * @MXL862XX_PMAPPER_MAPPING_PCP: Use PCP for VLAN tagged packets to derive 339 + * sub interface ID group 340 + * @MXL862XX_PMAPPER_MAPPING_LAG: Use LAG Index for Pmapper access 341 + * regardless of IP and VLAN packet 342 + * @MXL862XX_PMAPPER_MAPPING_DSCP: Use DSCP for VLAN tagged IP packets to 343 + * derive sub interface ID group 344 + */ 345 + enum mxl862xx_pmapper_mapping_mode { 346 + MXL862XX_PMAPPER_MAPPING_PCP = 0, 347 + MXL862XX_PMAPPER_MAPPING_LAG, 348 + MXL862XX_PMAPPER_MAPPING_DSCP, 349 + }; 350 + 351 + /** 352 + * struct mxl862xx_pmapper - P-mapper Configuration 353 + * @pmapper_id: Index of P-mapper (0-31) 354 + * @dest_sub_if_id_group: Sub interface ID group. Entry 0 is for non-IP and 355 + * non-VLAN tagged packets. 356 + * Entries 1-8 are PCP mapping entries for VLAN tagged 357 + * packets. 358 + * Entries 9-72 are DSCP or LAG mapping entries. 359 + * 360 + * Used by CTP port config and bridge port config. In case of LAG, it is 361 + * user's responsibility to provide the mapped entries in given P-mapper 362 + * table. In other modes the entries are auto mapped from input packet. 363 + */ 364 + struct mxl862xx_pmapper { 365 + __le16 pmapper_id; 366 + u8 dest_sub_if_id_group[73]; 367 + } __packed; 368 + 369 + /** 370 + * struct mxl862xx_bridge_port_config - Bridge Port Configuration 371 + * @bridge_port_id: Bridge Port ID allocated by bridge port allocation 372 + * @mask: See &enum mxl862xx_bridge_port_config_mask 373 + * @bridge_id: Bridge ID (FID) to which this bridge port is associated 374 + * @ingress_extended_vlan_enable: Enable extended VLAN processing for 375 + * ingress traffic 376 + * @ingress_extended_vlan_block_id: Extended VLAN block allocated for 377 + * ingress traffic 378 + * @ingress_extended_vlan_block_size: Extended VLAN block size for ingress 379 + * traffic 380 + * @egress_extended_vlan_enable: Enable extended VLAN processing for egress 381 + * traffic 382 + * @egress_extended_vlan_block_id: Extended VLAN block allocated for egress 383 + * traffic 384 + * @egress_extended_vlan_block_size: Extended VLAN block size for egress 385 + * traffic 386 + * @ingress_marking_mode: Ingress color marking mode. See 387 + * &enum mxl862xx_color_marking_mode 388 + * @egress_remarking_mode: Color remarking for egress traffic. See 389 + * &enum mxl862xx_color_remarking_mode 390 + * @ingress_metering_enable: Traffic metering on ingress traffic applies 391 + * @ingress_traffic_meter_id: Meter for ingress Bridge Port process 392 + * @egress_sub_metering_enable: Traffic metering on various types of egress 393 + * traffic 394 + * @egress_traffic_sub_meter_id: Meter for egress Bridge Port process with 395 + * specific type 396 + * @dest_logical_port_id: Destination logical port 397 + * @pmapper_enable: Enable P-mapper 398 + * @dest_sub_if_id_group: Destination sub interface ID group when 399 + * pmapper_enable is false 400 + * @pmapper_mapping_mode: P-mapper mapping mode. See 401 + * &enum mxl862xx_pmapper_mapping_mode 402 + * @pmapper_id_valid: When true, P-mapper is re-used; when false, 403 + * allocation is handled by API 404 + * @pmapper: P-mapper configuration used when pmapper_enable is true 405 + * @bridge_port_map: Port map defining broadcast domain. Each bit 406 + * represents one bridge port. Bridge port ID is 407 + * index * 16 + bit offset. 408 + * @mc_dest_ip_lookup_disable: Disable multicast IP destination table 409 + * lookup 410 + * @mc_src_ip_lookup_enable: Enable multicast IP source table lookup 411 + * @dest_mac_lookup_disable: Disable destination MAC lookup; packet treated 412 + * as unknown 413 + * @src_mac_learning_disable: Disable source MAC address learning 414 + * @mac_spoofing_detect_enable: Enable MAC spoofing detection 415 + * @port_lock_enable: Enable port locking 416 + * @mac_learning_limit_enable: Enable MAC learning limitation 417 + * @mac_learning_limit: Maximum number of MAC addresses that can be learned 418 + * from this bridge port 419 + * @loop_violation_count: Number of loop violation events from this bridge 420 + * port 421 + * @mac_learning_count: Number of MAC addresses learned from this bridge 422 + * port 423 + * @ingress_vlan_filter_enable: Enable ingress VLAN filter 424 + * @ingress_vlan_filter_block_id: VLAN filter block of ingress traffic 425 + * @ingress_vlan_filter_block_size: VLAN filter block size for ingress 426 + * traffic 427 + * @bypass_egress_vlan_filter1: For ingress traffic, bypass VLAN filter 1 428 + * at egress bridge port processing 429 + * @egress_vlan_filter1enable: Enable egress VLAN filter 1 430 + * @egress_vlan_filter1block_id: VLAN filter block 1 of egress traffic 431 + * @egress_vlan_filter1block_size: VLAN filter block 1 size 432 + * @egress_vlan_filter2enable: Enable egress VLAN filter 2 433 + * @egress_vlan_filter2block_id: VLAN filter block 2 of egress traffic 434 + * @egress_vlan_filter2block_size: VLAN filter block 2 size 435 + * @vlan_tag_selection: VLAN tag selection for MAC address/multicast 436 + * learning, lookup and filtering. 437 + * 0 - Intermediate outer VLAN tag is used. 438 + * 1 - Original outer VLAN tag is used. 439 + * @vlan_src_mac_priority_enable: Enable VLAN Priority field for source MAC 440 + * learning and filtering 441 + * @vlan_src_mac_dei_enable: Enable VLAN DEI/CFI field for source MAC 442 + * learning and filtering 443 + * @vlan_src_mac_vid_enable: Enable VLAN ID field for source MAC learning 444 + * and filtering 445 + * @vlan_dst_mac_priority_enable: Enable VLAN Priority field for destination 446 + * MAC lookup and filtering 447 + * @vlan_dst_mac_dei_enable: Enable VLAN CFI/DEI field for destination MAC 448 + * lookup and filtering 449 + * @vlan_dst_mac_vid_enable: Enable VLAN ID field for destination MAC lookup 450 + * and filtering 451 + * @vlan_multicast_priority_enable: Enable VLAN Priority field for IP 452 + * multicast lookup 453 + * @vlan_multicast_dei_enable: Enable VLAN CFI/DEI field for IP multicast 454 + * lookup 455 + * @vlan_multicast_vid_enable: Enable VLAN ID field for IP multicast lookup 456 + */ 457 + struct mxl862xx_bridge_port_config { 458 + __le16 bridge_port_id; 459 + __le32 mask; /* enum mxl862xx_bridge_port_config_mask */ 460 + __le16 bridge_id; 461 + u8 ingress_extended_vlan_enable; 462 + __le16 ingress_extended_vlan_block_id; 463 + __le16 ingress_extended_vlan_block_size; 464 + u8 egress_extended_vlan_enable; 465 + __le16 egress_extended_vlan_block_id; 466 + __le16 egress_extended_vlan_block_size; 467 + __le32 ingress_marking_mode; /* enum mxl862xx_color_marking_mode */ 468 + __le32 egress_remarking_mode; /* enum mxl862xx_color_remarking_mode */ 469 + u8 ingress_metering_enable; 470 + __le16 ingress_traffic_meter_id; 471 + u8 egress_sub_metering_enable[MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX]; 472 + __le16 egress_traffic_sub_meter_id[MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX]; 473 + u8 dest_logical_port_id; 474 + u8 pmapper_enable; 475 + __le16 dest_sub_if_id_group; 476 + __le32 pmapper_mapping_mode; /* enum mxl862xx_pmapper_mapping_mode */ 477 + u8 pmapper_id_valid; 478 + struct mxl862xx_pmapper pmapper; 479 + __le16 bridge_port_map[8]; 480 + u8 mc_dest_ip_lookup_disable; 481 + u8 mc_src_ip_lookup_enable; 482 + u8 dest_mac_lookup_disable; 483 + u8 src_mac_learning_disable; 484 + u8 mac_spoofing_detect_enable; 485 + u8 port_lock_enable; 486 + u8 mac_learning_limit_enable; 487 + __le16 mac_learning_limit; 488 + __le16 loop_violation_count; 489 + __le16 mac_learning_count; 490 + u8 ingress_vlan_filter_enable; 491 + __le16 ingress_vlan_filter_block_id; 492 + __le16 ingress_vlan_filter_block_size; 493 + u8 bypass_egress_vlan_filter1; 494 + u8 egress_vlan_filter1enable; 495 + __le16 egress_vlan_filter1block_id; 496 + __le16 egress_vlan_filter1block_size; 497 + u8 egress_vlan_filter2enable; 498 + __le16 egress_vlan_filter2block_id; 499 + __le16 egress_vlan_filter2block_size; 500 + u8 vlan_tag_selection; 501 + u8 vlan_src_mac_priority_enable; 502 + u8 vlan_src_mac_dei_enable; 503 + u8 vlan_src_mac_vid_enable; 504 + u8 vlan_dst_mac_priority_enable; 505 + u8 vlan_dst_mac_dei_enable; 506 + u8 vlan_dst_mac_vid_enable; 507 + u8 vlan_multicast_priority_enable; 508 + u8 vlan_multicast_dei_enable; 509 + u8 vlan_multicast_vid_enable; 510 + } __packed; 511 + 512 + /** 513 + * struct mxl862xx_cfg - Global Switch configuration Attributes 514 + * @mac_table_age_timer: See &enum mxl862xx_age_timer 515 + * @age_timer: Custom MAC table aging timer in seconds 516 + * @max_packet_len: Maximum Ethernet packet length 517 + * @learning_limit_action: Automatic MAC address table learning limitation 518 + * consecutive action 519 + * @mac_locking_action: Accept or discard MAC port locking violation 520 + * packets 521 + * @mac_spoofing_action: Accept or discard MAC spoofing and port MAC locking 522 + * violation packets 523 + * @pause_mac_mode_src: Pause frame MAC source address mode 524 + * @pause_mac_src: Pause frame MAC source address 525 + */ 526 + struct mxl862xx_cfg { 527 + __le32 mac_table_age_timer; /* enum mxl862xx_age_timer */ 528 + __le32 age_timer; 529 + __le16 max_packet_len; 530 + u8 learning_limit_action; 531 + u8 mac_locking_action; 532 + u8 mac_spoofing_action; 533 + u8 pause_mac_mode_src; 534 + u8 pause_mac_src[ETH_ALEN]; 535 + } __packed; 536 + 537 + /** 538 + * enum mxl862xx_ss_sp_tag_mask - Special tag valid field indicator bits 539 + * @MXL862XX_SS_SP_TAG_MASK_RX: valid RX special tag mode 540 + * @MXL862XX_SS_SP_TAG_MASK_TX: valid TX special tag mode 541 + * @MXL862XX_SS_SP_TAG_MASK_RX_PEN: valid RX special tag info over preamble 542 + * @MXL862XX_SS_SP_TAG_MASK_TX_PEN: valid TX special tag info over preamble 543 + */ 544 + enum mxl862xx_ss_sp_tag_mask { 545 + MXL862XX_SS_SP_TAG_MASK_RX = BIT(0), 546 + MXL862XX_SS_SP_TAG_MASK_TX = BIT(1), 547 + MXL862XX_SS_SP_TAG_MASK_RX_PEN = BIT(2), 548 + MXL862XX_SS_SP_TAG_MASK_TX_PEN = BIT(3), 549 + }; 550 + 551 + /** 552 + * enum mxl862xx_ss_sp_tag_rx - RX special tag mode 553 + * @MXL862XX_SS_SP_TAG_RX_NO_TAG_NO_INSERT: packet does NOT have special 554 + * tag and special tag is NOT inserted 555 + * @MXL862XX_SS_SP_TAG_RX_NO_TAG_INSERT: packet does NOT have special tag 556 + * and special tag is inserted 557 + * @MXL862XX_SS_SP_TAG_RX_TAG_NO_INSERT: packet has special tag and special 558 + * tag is NOT inserted 559 + */ 560 + enum mxl862xx_ss_sp_tag_rx { 561 + MXL862XX_SS_SP_TAG_RX_NO_TAG_NO_INSERT = 0, 562 + MXL862XX_SS_SP_TAG_RX_NO_TAG_INSERT = 1, 563 + MXL862XX_SS_SP_TAG_RX_TAG_NO_INSERT = 2, 564 + }; 565 + 566 + /** 567 + * enum mxl862xx_ss_sp_tag_tx - TX special tag mode 568 + * @MXL862XX_SS_SP_TAG_TX_NO_TAG_NO_REMOVE: packet does NOT have special 569 + * tag and special tag is NOT removed 570 + * @MXL862XX_SS_SP_TAG_TX_TAG_REPLACE: packet has special tag and special 571 + * tag is replaced 572 + * @MXL862XX_SS_SP_TAG_TX_TAG_NO_REMOVE: packet has special tag and special 573 + * tag is NOT removed 574 + * @MXL862XX_SS_SP_TAG_TX_TAG_REMOVE: packet has special tag and special 575 + * tag is removed 576 + */ 577 + enum mxl862xx_ss_sp_tag_tx { 578 + MXL862XX_SS_SP_TAG_TX_NO_TAG_NO_REMOVE = 0, 579 + MXL862XX_SS_SP_TAG_TX_TAG_REPLACE = 1, 580 + MXL862XX_SS_SP_TAG_TX_TAG_NO_REMOVE = 2, 581 + MXL862XX_SS_SP_TAG_TX_TAG_REMOVE = 3, 582 + }; 583 + 584 + /** 585 + * enum mxl862xx_ss_sp_tag_rx_pen - RX special tag info over preamble 586 + * @MXL862XX_SS_SP_TAG_RX_PEN_ALL_0: special tag info inserted from byte 2 587 + * to 7 are all 0 588 + * @MXL862XX_SS_SP_TAG_RX_PEN_BYTE_5_IS_16: special tag byte 5 is 16, other 589 + * bytes from 2 to 7 are 0 590 + * @MXL862XX_SS_SP_TAG_RX_PEN_BYTE_5_FROM_PREAMBLE: special tag byte 5 is 591 + * from preamble field, others 592 + * are 0 593 + * @MXL862XX_SS_SP_TAG_RX_PEN_BYTE_2_TO_7_FROM_PREAMBLE: special tag byte 2 594 + * to 7 are from preamble 595 + * field 596 + */ 597 + enum mxl862xx_ss_sp_tag_rx_pen { 598 + MXL862XX_SS_SP_TAG_RX_PEN_ALL_0 = 0, 599 + MXL862XX_SS_SP_TAG_RX_PEN_BYTE_5_IS_16 = 1, 600 + MXL862XX_SS_SP_TAG_RX_PEN_BYTE_5_FROM_PREAMBLE = 2, 601 + MXL862XX_SS_SP_TAG_RX_PEN_BYTE_2_TO_7_FROM_PREAMBLE = 3, 602 + }; 603 + 604 + /** 605 + * struct mxl862xx_ss_sp_tag - Special tag port settings 606 + * @pid: port ID (1~16) 607 + * @mask: See &enum mxl862xx_ss_sp_tag_mask 608 + * @rx: See &enum mxl862xx_ss_sp_tag_rx 609 + * @tx: See &enum mxl862xx_ss_sp_tag_tx 610 + * @rx_pen: See &enum mxl862xx_ss_sp_tag_rx_pen 611 + * @tx_pen: TX special tag info over preamble 612 + * 0 - disabled 613 + * 1 - enabled 614 + */ 615 + struct mxl862xx_ss_sp_tag { 616 + u8 pid; 617 + u8 mask; /* enum mxl862xx_ss_sp_tag_mask */ 618 + u8 rx; /* enum mxl862xx_ss_sp_tag_rx */ 619 + u8 tx; /* enum mxl862xx_ss_sp_tag_tx */ 620 + u8 rx_pen; /* enum mxl862xx_ss_sp_tag_rx_pen */ 621 + u8 tx_pen; /* boolean */ 622 + } __packed; 623 + 624 + /** 625 + * enum mxl862xx_logical_port_mode - Logical port mode 626 + * @MXL862XX_LOGICAL_PORT_8BIT_WLAN: WLAN with 8-bit station ID 627 + * @MXL862XX_LOGICAL_PORT_9BIT_WLAN: WLAN with 9-bit station ID 628 + * @MXL862XX_LOGICAL_PORT_ETHERNET: Ethernet port 629 + * @MXL862XX_LOGICAL_PORT_OTHER: Others 630 + */ 631 + enum mxl862xx_logical_port_mode { 632 + MXL862XX_LOGICAL_PORT_8BIT_WLAN = 0, 633 + MXL862XX_LOGICAL_PORT_9BIT_WLAN, 634 + MXL862XX_LOGICAL_PORT_ETHERNET, 635 + MXL862XX_LOGICAL_PORT_OTHER = 0xFF, 636 + }; 637 + 638 + /** 639 + * struct mxl862xx_ctp_port_assignment - CTP Port Assignment/association 640 + * with logical port 641 + * @logical_port_id: Logical Port Id. The valid range is hardware dependent 642 + * @first_ctp_port_id: First CTP (Connectivity Termination Port) ID mapped 643 + * to above logical port ID 644 + * @number_of_ctp_port: Total number of CTP Ports mapped above logical port 645 + * ID 646 + * @mode: Logical port mode to define sub interface ID format. See 647 + * &enum mxl862xx_logical_port_mode 648 + * @bridge_port_id: Bridge Port ID (not FID). For allocation, each CTP 649 + * allocated is mapped to the Bridge Port given by this field. 650 + * The Bridge Port will be configured to use first CTP as 651 + * egress CTP. 652 + */ 653 + struct mxl862xx_ctp_port_assignment { 654 + u8 logical_port_id; 655 + __le16 first_ctp_port_id; 656 + __le16 number_of_ctp_port; 657 + __le32 mode; /* enum mxl862xx_logical_port_mode */ 658 + __le16 bridge_port_id; 659 + } __packed; 660 + 661 + /** 662 + * struct mxl862xx_sys_fw_image_version - Firmware version information 663 + * @iv_major: firmware major version 664 + * @iv_minor: firmware minor version 665 + * @iv_revision: firmware revision 666 + * @iv_build_num: firmware build number 667 + */ 668 + struct mxl862xx_sys_fw_image_version { 669 + u8 iv_major; 670 + u8 iv_minor; 671 + __le16 iv_revision; 672 + __le32 iv_build_num; 673 + } __packed; 674 + 675 + #endif /* __MXL862XX_API_H */
+49
drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + 3 + #ifndef __MXL862XX_CMD_H 4 + #define __MXL862XX_CMD_H 5 + 6 + #define MXL862XX_MMD_DEV 30 7 + #define MXL862XX_MMD_REG_CTRL 0 8 + #define MXL862XX_MMD_REG_LEN_RET 1 9 + #define MXL862XX_MMD_REG_DATA_FIRST 2 10 + #define MXL862XX_MMD_REG_DATA_LAST 95 11 + #define MXL862XX_MMD_REG_DATA_MAX_SIZE \ 12 + (MXL862XX_MMD_REG_DATA_LAST - MXL862XX_MMD_REG_DATA_FIRST + 1) 13 + 14 + #define MXL862XX_COMMON_MAGIC 0x100 15 + #define MXL862XX_BRDG_MAGIC 0x300 16 + #define MXL862XX_BRDGPORT_MAGIC 0x400 17 + #define MXL862XX_CTP_MAGIC 0x500 18 + #define MXL862XX_SWMAC_MAGIC 0xa00 19 + #define MXL862XX_SS_MAGIC 0x1600 20 + #define GPY_GPY2XX_MAGIC 0x1800 21 + #define SYS_MISC_MAGIC 0x1900 22 + 23 + #define MXL862XX_COMMON_CFGGET (MXL862XX_COMMON_MAGIC + 0x9) 24 + #define MXL862XX_COMMON_REGISTERMOD (MXL862XX_COMMON_MAGIC + 0x11) 25 + 26 + #define MXL862XX_BRIDGE_ALLOC (MXL862XX_BRDG_MAGIC + 0x1) 27 + #define MXL862XX_BRIDGE_CONFIGSET (MXL862XX_BRDG_MAGIC + 0x2) 28 + #define MXL862XX_BRIDGE_CONFIGGET (MXL862XX_BRDG_MAGIC + 0x3) 29 + #define MXL862XX_BRIDGE_FREE (MXL862XX_BRDG_MAGIC + 0x4) 30 + 31 + #define MXL862XX_BRIDGEPORT_ALLOC (MXL862XX_BRDGPORT_MAGIC + 0x1) 32 + #define MXL862XX_BRIDGEPORT_CONFIGSET (MXL862XX_BRDGPORT_MAGIC + 0x2) 33 + #define MXL862XX_BRIDGEPORT_CONFIGGET (MXL862XX_BRDGPORT_MAGIC + 0x3) 34 + #define MXL862XX_BRIDGEPORT_FREE (MXL862XX_BRDGPORT_MAGIC + 0x4) 35 + 36 + #define MXL862XX_CTP_PORTASSIGNMENTSET (MXL862XX_CTP_MAGIC + 0x3) 37 + 38 + #define MXL862XX_MAC_TABLECLEARCOND (MXL862XX_SWMAC_MAGIC + 0x8) 39 + 40 + #define MXL862XX_SS_SPTAG_SET (MXL862XX_SS_MAGIC + 0x02) 41 + 42 + #define INT_GPHY_READ (GPY_GPY2XX_MAGIC + 0x01) 43 + #define INT_GPHY_WRITE (GPY_GPY2XX_MAGIC + 0x02) 44 + 45 + #define SYS_MISC_FW_VERSION (SYS_MISC_MAGIC + 0x02) 46 + 47 + #define MMD_API_MAXIMUM_ID 0x7fff 48 + 49 + #endif /* __MXL862XX_CMD_H */
+245
drivers/net/dsa/mxl862xx/mxl862xx-host.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Based upon the MaxLinear SDK driver 4 + * 5 + * Copyright (C) 2025 Daniel Golle <daniel@makrotopia.org> 6 + * Copyright (C) 2025 John Crispin <john@phrozen.org> 7 + * Copyright (C) 2024 MaxLinear Inc. 8 + */ 9 + 10 + #include <linux/bits.h> 11 + #include <linux/iopoll.h> 12 + #include <linux/limits.h> 13 + #include <net/dsa.h> 14 + #include "mxl862xx.h" 15 + #include "mxl862xx-host.h" 16 + 17 + #define CTRL_BUSY_MASK BIT(15) 18 + 19 + #define MXL862XX_MMD_REG_CTRL 0 20 + #define MXL862XX_MMD_REG_LEN_RET 1 21 + #define MXL862XX_MMD_REG_DATA_FIRST 2 22 + #define MXL862XX_MMD_REG_DATA_LAST 95 23 + #define MXL862XX_MMD_REG_DATA_MAX_SIZE \ 24 + (MXL862XX_MMD_REG_DATA_LAST - MXL862XX_MMD_REG_DATA_FIRST + 1) 25 + 26 + #define MMD_API_SET_DATA_0 2 27 + #define MMD_API_GET_DATA_0 5 28 + #define MMD_API_RST_DATA 8 29 + 30 + #define MXL862XX_SWITCH_RESET 0x9907 31 + 32 + static int mxl862xx_reg_read(struct mxl862xx_priv *priv, u32 addr) 33 + { 34 + return __mdiodev_c45_read(priv->mdiodev, MDIO_MMD_VEND1, addr); 35 + } 36 + 37 + static int mxl862xx_reg_write(struct mxl862xx_priv *priv, u32 addr, u16 data) 38 + { 39 + return __mdiodev_c45_write(priv->mdiodev, MDIO_MMD_VEND1, addr, data); 40 + } 41 + 42 + static int mxl862xx_ctrl_read(struct mxl862xx_priv *priv) 43 + { 44 + return mxl862xx_reg_read(priv, MXL862XX_MMD_REG_CTRL); 45 + } 46 + 47 + static int mxl862xx_busy_wait(struct mxl862xx_priv *priv) 48 + { 49 + int val; 50 + 51 + return readx_poll_timeout(mxl862xx_ctrl_read, priv, val, 52 + !(val & CTRL_BUSY_MASK), 15, 500000); 53 + } 54 + 55 + static int mxl862xx_set_data(struct mxl862xx_priv *priv, u16 words) 56 + { 57 + int ret; 58 + u16 cmd; 59 + 60 + ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, 61 + MXL862XX_MMD_REG_DATA_MAX_SIZE * sizeof(u16)); 62 + if (ret < 0) 63 + return ret; 64 + 65 + cmd = words / MXL862XX_MMD_REG_DATA_MAX_SIZE - 1; 66 + if (!(cmd < 2)) 67 + return -EINVAL; 68 + 69 + cmd += MMD_API_SET_DATA_0; 70 + ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL, 71 + cmd | CTRL_BUSY_MASK); 72 + if (ret < 0) 73 + return ret; 74 + 75 + return mxl862xx_busy_wait(priv); 76 + } 77 + 78 + static int mxl862xx_get_data(struct mxl862xx_priv *priv, u16 words) 79 + { 80 + int ret; 81 + u16 cmd; 82 + 83 + ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, 84 + MXL862XX_MMD_REG_DATA_MAX_SIZE * sizeof(u16)); 85 + if (ret < 0) 86 + return ret; 87 + 88 + cmd = words / MXL862XX_MMD_REG_DATA_MAX_SIZE; 89 + if (!(cmd > 0 && cmd < 3)) 90 + return -EINVAL; 91 + 92 + cmd += MMD_API_GET_DATA_0; 93 + ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL, 94 + cmd | CTRL_BUSY_MASK); 95 + if (ret < 0) 96 + return ret; 97 + 98 + return mxl862xx_busy_wait(priv); 99 + } 100 + 101 + static int mxl862xx_firmware_return(int ret) 102 + { 103 + /* Only 16-bit values are valid. */ 104 + if (WARN_ON(ret & GENMASK(31, 16))) 105 + return -EINVAL; 106 + 107 + /* Interpret value as signed 16-bit integer. */ 108 + return (s16)ret; 109 + } 110 + 111 + static int mxl862xx_send_cmd(struct mxl862xx_priv *priv, u16 cmd, u16 size, 112 + bool quiet) 113 + { 114 + int ret; 115 + 116 + ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, size); 117 + if (ret) 118 + return ret; 119 + 120 + ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL, 121 + cmd | CTRL_BUSY_MASK); 122 + if (ret) 123 + return ret; 124 + 125 + ret = mxl862xx_busy_wait(priv); 126 + if (ret) 127 + return ret; 128 + 129 + ret = mxl862xx_reg_read(priv, MXL862XX_MMD_REG_LEN_RET); 130 + if (ret < 0) 131 + return ret; 132 + 133 + /* handle errors returned by the firmware as -EIO 134 + * The firmware is based on Zephyr OS and uses the errors as 135 + * defined in errno.h of Zephyr OS. See 136 + * https://github.com/zephyrproject-rtos/zephyr/blob/v3.7.0/lib/libc/minimal/include/errno.h 137 + */ 138 + ret = mxl862xx_firmware_return(ret); 139 + if (ret < 0) { 140 + if (!quiet) 141 + dev_err(&priv->mdiodev->dev, 142 + "CMD %04x returned error %d\n", cmd, ret); 143 + return -EIO; 144 + } 145 + 146 + return ret; 147 + } 148 + 149 + int mxl862xx_api_wrap(struct mxl862xx_priv *priv, u16 cmd, void *_data, 150 + u16 size, bool read, bool quiet) 151 + { 152 + __le16 *data = _data; 153 + int ret, cmd_ret; 154 + u16 max, i; 155 + 156 + dev_dbg(&priv->mdiodev->dev, "CMD %04x DATA %*ph\n", cmd, size, data); 157 + 158 + mutex_lock_nested(&priv->mdiodev->bus->mdio_lock, MDIO_MUTEX_NESTED); 159 + 160 + max = (size + 1) / 2; 161 + 162 + ret = mxl862xx_busy_wait(priv); 163 + if (ret < 0) 164 + goto out; 165 + 166 + for (i = 0; i < max; i++) { 167 + u16 off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE; 168 + 169 + if (i && off == 0) { 170 + /* Send command to set data when every 171 + * MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs are written. 172 + */ 173 + ret = mxl862xx_set_data(priv, i); 174 + if (ret < 0) 175 + goto out; 176 + } 177 + 178 + ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_DATA_FIRST + off, 179 + le16_to_cpu(data[i])); 180 + if (ret < 0) 181 + goto out; 182 + } 183 + 184 + ret = mxl862xx_send_cmd(priv, cmd, size, quiet); 185 + if (ret < 0 || !read) 186 + goto out; 187 + 188 + /* store result of mxl862xx_send_cmd() */ 189 + cmd_ret = ret; 190 + 191 + for (i = 0; i < max; i++) { 192 + u16 off = i % MXL862XX_MMD_REG_DATA_MAX_SIZE; 193 + 194 + if (i && off == 0) { 195 + /* Send command to fetch next batch of data when every 196 + * MXL862XX_MMD_REG_DATA_MAX_SIZE of WORDs are read. 197 + */ 198 + ret = mxl862xx_get_data(priv, i); 199 + if (ret < 0) 200 + goto out; 201 + } 202 + 203 + ret = mxl862xx_reg_read(priv, MXL862XX_MMD_REG_DATA_FIRST + off); 204 + if (ret < 0) 205 + goto out; 206 + 207 + if ((i * 2 + 1) == size) { 208 + /* Special handling for last BYTE if it's not WORD 209 + * aligned to avoid writing beyond the allocated data 210 + * structure. 211 + */ 212 + *(uint8_t *)&data[i] = ret & 0xff; 213 + } else { 214 + data[i] = cpu_to_le16((u16)ret); 215 + } 216 + } 217 + 218 + /* on success return the result of the mxl862xx_send_cmd() */ 219 + ret = cmd_ret; 220 + 221 + dev_dbg(&priv->mdiodev->dev, "RET %d DATA %*ph\n", ret, size, data); 222 + 223 + out: 224 + mutex_unlock(&priv->mdiodev->bus->mdio_lock); 225 + 226 + return ret; 227 + } 228 + 229 + int mxl862xx_reset(struct mxl862xx_priv *priv) 230 + { 231 + int ret; 232 + 233 + mutex_lock_nested(&priv->mdiodev->bus->mdio_lock, MDIO_MUTEX_NESTED); 234 + 235 + /* Software reset */ 236 + ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, 0); 237 + if (ret) 238 + goto out; 239 + 240 + ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL, MXL862XX_SWITCH_RESET); 241 + out: 242 + mutex_unlock(&priv->mdiodev->bus->mdio_lock); 243 + 244 + return ret; 245 + }
+12
drivers/net/dsa/mxl862xx/mxl862xx-host.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + 3 + #ifndef __MXL862XX_HOST_H 4 + #define __MXL862XX_HOST_H 5 + 6 + #include "mxl862xx.h" 7 + 8 + int mxl862xx_api_wrap(struct mxl862xx_priv *priv, u16 cmd, void *data, u16 size, 9 + bool read, bool quiet); 10 + int mxl862xx_reset(struct mxl862xx_priv *priv); 11 + 12 + #endif /* __MXL862XX_HOST_H */
+476
drivers/net/dsa/mxl862xx/mxl862xx.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * Driver for MaxLinear MxL862xx switch family 4 + * 5 + * Copyright (C) 2024 MaxLinear Inc. 6 + * Copyright (C) 2025 John Crispin <john@phrozen.org> 7 + * Copyright (C) 2025 Daniel Golle <daniel@makrotopia.org> 8 + */ 9 + 10 + #include <linux/module.h> 11 + #include <linux/delay.h> 12 + #include <linux/of_device.h> 13 + #include <linux/of_mdio.h> 14 + #include <linux/phy.h> 15 + #include <linux/phylink.h> 16 + #include <net/dsa.h> 17 + 18 + #include "mxl862xx.h" 19 + #include "mxl862xx-api.h" 20 + #include "mxl862xx-cmd.h" 21 + #include "mxl862xx-host.h" 22 + 23 + #define MXL862XX_API_WRITE(dev, cmd, data) \ 24 + mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), false, false) 25 + #define MXL862XX_API_READ(dev, cmd, data) \ 26 + mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), true, false) 27 + #define MXL862XX_API_READ_QUIET(dev, cmd, data) \ 28 + mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), true, true) 29 + 30 + #define MXL862XX_SDMA_PCTRLP(p) (0xbc0 + ((p) * 0x6)) 31 + #define MXL862XX_SDMA_PCTRL_EN BIT(0) 32 + 33 + #define MXL862XX_FDMA_PCTRLP(p) (0xa80 + ((p) * 0x6)) 34 + #define MXL862XX_FDMA_PCTRL_EN BIT(0) 35 + 36 + #define MXL862XX_READY_TIMEOUT_MS 10000 37 + #define MXL862XX_READY_POLL_MS 100 38 + 39 + static enum dsa_tag_protocol mxl862xx_get_tag_protocol(struct dsa_switch *ds, 40 + int port, 41 + enum dsa_tag_protocol m) 42 + { 43 + return DSA_TAG_PROTO_MXL862; 44 + } 45 + 46 + /* PHY access via firmware relay */ 47 + static int mxl862xx_phy_read_mmd(struct mxl862xx_priv *priv, int port, 48 + int devadd, int reg) 49 + { 50 + struct mdio_relay_data param = { 51 + .phy = port, 52 + .mmd = devadd, 53 + .reg = cpu_to_le16(reg), 54 + }; 55 + int ret; 56 + 57 + ret = MXL862XX_API_READ(priv, INT_GPHY_READ, param); 58 + if (ret) 59 + return ret; 60 + 61 + return le16_to_cpu(param.data); 62 + } 63 + 64 + static int mxl862xx_phy_write_mmd(struct mxl862xx_priv *priv, int port, 65 + int devadd, int reg, u16 data) 66 + { 67 + struct mdio_relay_data param = { 68 + .phy = port, 69 + .mmd = devadd, 70 + .reg = cpu_to_le16(reg), 71 + .data = cpu_to_le16(data), 72 + }; 73 + 74 + return MXL862XX_API_WRITE(priv, INT_GPHY_WRITE, param); 75 + } 76 + 77 + static int mxl862xx_phy_read_mii_bus(struct mii_bus *bus, int port, int regnum) 78 + { 79 + return mxl862xx_phy_read_mmd(bus->priv, port, 0, regnum); 80 + } 81 + 82 + static int mxl862xx_phy_write_mii_bus(struct mii_bus *bus, int port, 83 + int regnum, u16 val) 84 + { 85 + return mxl862xx_phy_write_mmd(bus->priv, port, 0, regnum, val); 86 + } 87 + 88 + static int mxl862xx_phy_read_c45_mii_bus(struct mii_bus *bus, int port, 89 + int devadd, int regnum) 90 + { 91 + return mxl862xx_phy_read_mmd(bus->priv, port, devadd, regnum); 92 + } 93 + 94 + static int mxl862xx_phy_write_c45_mii_bus(struct mii_bus *bus, int port, 95 + int devadd, int regnum, u16 val) 96 + { 97 + return mxl862xx_phy_write_mmd(bus->priv, port, devadd, regnum, val); 98 + } 99 + 100 + static int mxl862xx_wait_ready(struct dsa_switch *ds) 101 + { 102 + struct mxl862xx_sys_fw_image_version ver = {}; 103 + unsigned long start = jiffies, timeout; 104 + struct mxl862xx_priv *priv = ds->priv; 105 + struct mxl862xx_cfg cfg = {}; 106 + int ret; 107 + 108 + timeout = start + msecs_to_jiffies(MXL862XX_READY_TIMEOUT_MS); 109 + msleep(2000); /* it always takes at least 2 seconds */ 110 + do { 111 + ret = MXL862XX_API_READ_QUIET(priv, SYS_MISC_FW_VERSION, ver); 112 + if (ret || !ver.iv_major) 113 + goto not_ready_yet; 114 + 115 + /* being able to perform CFGGET indicates that 116 + * the firmware is ready 117 + */ 118 + ret = MXL862XX_API_READ_QUIET(priv, 119 + MXL862XX_COMMON_CFGGET, 120 + cfg); 121 + if (ret) 122 + goto not_ready_yet; 123 + 124 + dev_info(ds->dev, "switch ready after %ums, firmware %u.%u.%u (build %u)\n", 125 + jiffies_to_msecs(jiffies - start), 126 + ver.iv_major, ver.iv_minor, 127 + le16_to_cpu(ver.iv_revision), 128 + le32_to_cpu(ver.iv_build_num)); 129 + return 0; 130 + 131 + not_ready_yet: 132 + msleep(MXL862XX_READY_POLL_MS); 133 + } while (time_before(jiffies, timeout)); 134 + 135 + dev_err(ds->dev, "switch not responding after reset\n"); 136 + return -ETIMEDOUT; 137 + } 138 + 139 + static int mxl862xx_setup_mdio(struct dsa_switch *ds) 140 + { 141 + struct mxl862xx_priv *priv = ds->priv; 142 + struct device *dev = ds->dev; 143 + struct device_node *mdio_np; 144 + struct mii_bus *bus; 145 + int ret; 146 + 147 + bus = devm_mdiobus_alloc(dev); 148 + if (!bus) 149 + return -ENOMEM; 150 + 151 + bus->priv = priv; 152 + ds->user_mii_bus = bus; 153 + bus->name = KBUILD_MODNAME "-mii"; 154 + snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(dev)); 155 + bus->read_c45 = mxl862xx_phy_read_c45_mii_bus; 156 + bus->write_c45 = mxl862xx_phy_write_c45_mii_bus; 157 + bus->read = mxl862xx_phy_read_mii_bus; 158 + bus->write = mxl862xx_phy_write_mii_bus; 159 + bus->parent = dev; 160 + bus->phy_mask = ~ds->phys_mii_mask; 161 + 162 + mdio_np = of_get_child_by_name(dev->of_node, "mdio"); 163 + if (!mdio_np) 164 + return -ENODEV; 165 + 166 + ret = devm_of_mdiobus_register(dev, bus, mdio_np); 167 + of_node_put(mdio_np); 168 + 169 + return ret; 170 + } 171 + 172 + static int mxl862xx_setup(struct dsa_switch *ds) 173 + { 174 + struct mxl862xx_priv *priv = ds->priv; 175 + int ret; 176 + 177 + ret = mxl862xx_reset(priv); 178 + if (ret) 179 + return ret; 180 + 181 + ret = mxl862xx_wait_ready(ds); 182 + if (ret) 183 + return ret; 184 + 185 + return mxl862xx_setup_mdio(ds); 186 + } 187 + 188 + static int mxl862xx_port_state(struct dsa_switch *ds, int port, bool enable) 189 + { 190 + struct mxl862xx_register_mod sdma = { 191 + .addr = cpu_to_le16(MXL862XX_SDMA_PCTRLP(port)), 192 + .data = cpu_to_le16(enable ? MXL862XX_SDMA_PCTRL_EN : 0), 193 + .mask = cpu_to_le16(MXL862XX_SDMA_PCTRL_EN), 194 + }; 195 + struct mxl862xx_register_mod fdma = { 196 + .addr = cpu_to_le16(MXL862XX_FDMA_PCTRLP(port)), 197 + .data = cpu_to_le16(enable ? MXL862XX_FDMA_PCTRL_EN : 0), 198 + .mask = cpu_to_le16(MXL862XX_FDMA_PCTRL_EN), 199 + }; 200 + int ret; 201 + 202 + ret = MXL862XX_API_WRITE(ds->priv, MXL862XX_COMMON_REGISTERMOD, sdma); 203 + if (ret) 204 + return ret; 205 + 206 + return MXL862XX_API_WRITE(ds->priv, MXL862XX_COMMON_REGISTERMOD, fdma); 207 + } 208 + 209 + static int mxl862xx_port_enable(struct dsa_switch *ds, int port, 210 + struct phy_device *phydev) 211 + { 212 + return mxl862xx_port_state(ds, port, true); 213 + } 214 + 215 + static void mxl862xx_port_disable(struct dsa_switch *ds, int port) 216 + { 217 + if (mxl862xx_port_state(ds, port, false)) 218 + dev_err(ds->dev, "failed to disable port %d\n", port); 219 + } 220 + 221 + static void mxl862xx_port_fast_age(struct dsa_switch *ds, int port) 222 + { 223 + struct mxl862xx_mac_table_clear param = { 224 + .type = MXL862XX_MAC_CLEAR_PHY_PORT, 225 + .port_id = port, 226 + }; 227 + 228 + if (MXL862XX_API_WRITE(ds->priv, MXL862XX_MAC_TABLECLEARCOND, param)) 229 + dev_err(ds->dev, "failed to clear fdb on port %d\n", port); 230 + } 231 + 232 + static int mxl862xx_configure_ctp_port(struct dsa_switch *ds, int port, 233 + u16 first_ctp_port_id, 234 + u16 number_of_ctp_ports) 235 + { 236 + struct mxl862xx_ctp_port_assignment ctp_assign = { 237 + .logical_port_id = port, 238 + .first_ctp_port_id = cpu_to_le16(first_ctp_port_id), 239 + .number_of_ctp_port = cpu_to_le16(number_of_ctp_ports), 240 + .mode = cpu_to_le32(MXL862XX_LOGICAL_PORT_ETHERNET), 241 + }; 242 + 243 + return MXL862XX_API_WRITE(ds->priv, MXL862XX_CTP_PORTASSIGNMENTSET, 244 + ctp_assign); 245 + } 246 + 247 + static int mxl862xx_configure_sp_tag_proto(struct dsa_switch *ds, int port, 248 + bool enable) 249 + { 250 + struct mxl862xx_ss_sp_tag tag = { 251 + .pid = port, 252 + .mask = MXL862XX_SS_SP_TAG_MASK_RX | MXL862XX_SS_SP_TAG_MASK_TX, 253 + .rx = enable ? MXL862XX_SS_SP_TAG_RX_TAG_NO_INSERT : 254 + MXL862XX_SS_SP_TAG_RX_NO_TAG_INSERT, 255 + .tx = enable ? MXL862XX_SS_SP_TAG_TX_TAG_NO_REMOVE : 256 + MXL862XX_SS_SP_TAG_TX_TAG_REMOVE, 257 + }; 258 + 259 + return MXL862XX_API_WRITE(ds->priv, MXL862XX_SS_SPTAG_SET, tag); 260 + } 261 + 262 + static int mxl862xx_setup_cpu_bridge(struct dsa_switch *ds, int port) 263 + { 264 + struct mxl862xx_bridge_port_config br_port_cfg = {}; 265 + struct mxl862xx_priv *priv = ds->priv; 266 + u16 bridge_port_map = 0; 267 + struct dsa_port *dp; 268 + 269 + /* CPU port bridge setup */ 270 + br_port_cfg.mask = cpu_to_le32(MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP | 271 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING | 272 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MAC_LEARNING); 273 + 274 + br_port_cfg.bridge_port_id = cpu_to_le16(port); 275 + br_port_cfg.src_mac_learning_disable = false; 276 + br_port_cfg.vlan_src_mac_vid_enable = true; 277 + br_port_cfg.vlan_dst_mac_vid_enable = true; 278 + 279 + /* include all assigned user ports in the CPU portmap */ 280 + dsa_switch_for_each_user_port(dp, ds) { 281 + /* it's safe to rely on cpu_dp being valid for user ports */ 282 + if (dp->cpu_dp->index != port) 283 + continue; 284 + 285 + bridge_port_map |= BIT(dp->index); 286 + } 287 + br_port_cfg.bridge_port_map[0] |= cpu_to_le16(bridge_port_map); 288 + 289 + return MXL862XX_API_WRITE(priv, MXL862XX_BRIDGEPORT_CONFIGSET, br_port_cfg); 290 + } 291 + 292 + static int mxl862xx_add_single_port_bridge(struct dsa_switch *ds, int port) 293 + { 294 + struct mxl862xx_bridge_port_config br_port_cfg = {}; 295 + struct dsa_port *dp = dsa_to_port(ds, port); 296 + struct mxl862xx_bridge_alloc br_alloc = {}; 297 + int ret; 298 + 299 + ret = MXL862XX_API_READ(ds->priv, MXL862XX_BRIDGE_ALLOC, br_alloc); 300 + if (ret) { 301 + dev_err(ds->dev, "failed to allocate a bridge for port %d\n", port); 302 + return ret; 303 + } 304 + 305 + br_port_cfg.bridge_id = br_alloc.bridge_id; 306 + br_port_cfg.bridge_port_id = cpu_to_le16(port); 307 + br_port_cfg.mask = cpu_to_le32(MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID | 308 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP | 309 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING | 310 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MAC_LEARNING); 311 + br_port_cfg.src_mac_learning_disable = true; 312 + br_port_cfg.vlan_src_mac_vid_enable = false; 313 + br_port_cfg.vlan_dst_mac_vid_enable = false; 314 + /* As this function is only called for user ports it is safe to rely on 315 + * cpu_dp being valid 316 + */ 317 + br_port_cfg.bridge_port_map[0] = cpu_to_le16(BIT(dp->cpu_dp->index)); 318 + 319 + return MXL862XX_API_WRITE(ds->priv, MXL862XX_BRIDGEPORT_CONFIGSET, br_port_cfg); 320 + } 321 + 322 + static int mxl862xx_port_setup(struct dsa_switch *ds, int port) 323 + { 324 + struct dsa_port *dp = dsa_to_port(ds, port); 325 + bool is_cpu_port = dsa_port_is_cpu(dp); 326 + int ret; 327 + 328 + /* disable port and flush MAC entries */ 329 + ret = mxl862xx_port_state(ds, port, false); 330 + if (ret) 331 + return ret; 332 + 333 + mxl862xx_port_fast_age(ds, port); 334 + 335 + /* skip setup for unused and DSA ports */ 336 + if (dsa_port_is_unused(dp) || 337 + dsa_port_is_dsa(dp)) 338 + return 0; 339 + 340 + /* configure tag protocol */ 341 + ret = mxl862xx_configure_sp_tag_proto(ds, port, is_cpu_port); 342 + if (ret) 343 + return ret; 344 + 345 + /* assign CTP port IDs */ 346 + ret = mxl862xx_configure_ctp_port(ds, port, port, 347 + is_cpu_port ? 32 - port : 1); 348 + if (ret) 349 + return ret; 350 + 351 + if (is_cpu_port) 352 + /* assign user ports to CPU port bridge */ 353 + return mxl862xx_setup_cpu_bridge(ds, port); 354 + 355 + /* setup single-port bridge for user ports */ 356 + return mxl862xx_add_single_port_bridge(ds, port); 357 + } 358 + 359 + static void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port, 360 + struct phylink_config *config) 361 + { 362 + config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 | 363 + MAC_100 | MAC_1000 | MAC_2500FD; 364 + 365 + __set_bit(PHY_INTERFACE_MODE_INTERNAL, 366 + config->supported_interfaces); 367 + } 368 + 369 + static const struct dsa_switch_ops mxl862xx_switch_ops = { 370 + .get_tag_protocol = mxl862xx_get_tag_protocol, 371 + .setup = mxl862xx_setup, 372 + .port_setup = mxl862xx_port_setup, 373 + .phylink_get_caps = mxl862xx_phylink_get_caps, 374 + .port_enable = mxl862xx_port_enable, 375 + .port_disable = mxl862xx_port_disable, 376 + .port_fast_age = mxl862xx_port_fast_age, 377 + }; 378 + 379 + static void mxl862xx_phylink_mac_config(struct phylink_config *config, 380 + unsigned int mode, 381 + const struct phylink_link_state *state) 382 + { 383 + } 384 + 385 + static void mxl862xx_phylink_mac_link_down(struct phylink_config *config, 386 + unsigned int mode, 387 + phy_interface_t interface) 388 + { 389 + } 390 + 391 + static void mxl862xx_phylink_mac_link_up(struct phylink_config *config, 392 + struct phy_device *phydev, 393 + unsigned int mode, 394 + phy_interface_t interface, 395 + int speed, int duplex, 396 + bool tx_pause, bool rx_pause) 397 + { 398 + } 399 + 400 + static const struct phylink_mac_ops mxl862xx_phylink_mac_ops = { 401 + .mac_config = mxl862xx_phylink_mac_config, 402 + .mac_link_down = mxl862xx_phylink_mac_link_down, 403 + .mac_link_up = mxl862xx_phylink_mac_link_up, 404 + }; 405 + 406 + static int mxl862xx_probe(struct mdio_device *mdiodev) 407 + { 408 + struct device *dev = &mdiodev->dev; 409 + struct mxl862xx_priv *priv; 410 + struct dsa_switch *ds; 411 + 412 + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 413 + if (!priv) 414 + return -ENOMEM; 415 + 416 + priv->mdiodev = mdiodev; 417 + 418 + ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL); 419 + if (!ds) 420 + return -ENOMEM; 421 + 422 + priv->ds = ds; 423 + ds->dev = dev; 424 + ds->priv = priv; 425 + ds->ops = &mxl862xx_switch_ops; 426 + ds->phylink_mac_ops = &mxl862xx_phylink_mac_ops; 427 + ds->num_ports = MXL862XX_MAX_PORTS; 428 + 429 + dev_set_drvdata(dev, ds); 430 + 431 + return dsa_register_switch(ds); 432 + } 433 + 434 + static void mxl862xx_remove(struct mdio_device *mdiodev) 435 + { 436 + struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev); 437 + 438 + if (!ds) 439 + return; 440 + 441 + dsa_unregister_switch(ds); 442 + } 443 + 444 + static void mxl862xx_shutdown(struct mdio_device *mdiodev) 445 + { 446 + struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev); 447 + 448 + if (!ds) 449 + return; 450 + 451 + dsa_switch_shutdown(ds); 452 + 453 + dev_set_drvdata(&mdiodev->dev, NULL); 454 + } 455 + 456 + static const struct of_device_id mxl862xx_of_match[] = { 457 + { .compatible = "maxlinear,mxl86282" }, 458 + { .compatible = "maxlinear,mxl86252" }, 459 + { /* sentinel */ } 460 + }; 461 + MODULE_DEVICE_TABLE(of, mxl862xx_of_match); 462 + 463 + static struct mdio_driver mxl862xx_driver = { 464 + .probe = mxl862xx_probe, 465 + .remove = mxl862xx_remove, 466 + .shutdown = mxl862xx_shutdown, 467 + .mdiodrv.driver = { 468 + .name = "mxl862xx", 469 + .of_match_table = mxl862xx_of_match, 470 + }, 471 + }; 472 + 473 + mdio_module_driver(mxl862xx_driver); 474 + 475 + MODULE_DESCRIPTION("Driver for MaxLinear MxL862xx switch family"); 476 + MODULE_LICENSE("GPL");
+16
drivers/net/dsa/mxl862xx/mxl862xx.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-or-later */ 2 + 3 + #ifndef __MXL862XX_H 4 + #define __MXL862XX_H 5 + 6 + #include <linux/mdio.h> 7 + #include <net/dsa.h> 8 + 9 + #define MXL862XX_MAX_PORTS 17 10 + 11 + struct mxl862xx_priv { 12 + struct dsa_switch *ds; 13 + struct mdio_device *mdiodev; 14 + }; 15 + 16 + #endif /* __MXL862XX_H */
+13
include/linux/mdio.h
··· 647 647 mask, set); 648 648 } 649 649 650 + static inline int __mdiodev_c45_read(struct mdio_device *mdiodev, int devad, 651 + u16 regnum) 652 + { 653 + return __mdiobus_c45_read(mdiodev->bus, mdiodev->addr, devad, regnum); 654 + } 655 + 656 + static inline int __mdiodev_c45_write(struct mdio_device *mdiodev, u32 devad, 657 + u16 regnum, u16 val) 658 + { 659 + return __mdiobus_c45_write(mdiodev->bus, mdiodev->addr, devad, regnum, 660 + val); 661 + } 662 + 650 663 static inline int mdiodev_c45_modify(struct mdio_device *mdiodev, int devad, 651 664 u32 regnum, u16 mask, u16 set) 652 665 {
+2
include/net/dsa.h
··· 57 57 #define DSA_TAG_PROTO_BRCM_LEGACY_FCS_VALUE 29 58 58 #define DSA_TAG_PROTO_YT921X_VALUE 30 59 59 #define DSA_TAG_PROTO_MXL_GSW1XX_VALUE 31 60 + #define DSA_TAG_PROTO_MXL862_VALUE 32 60 61 61 62 enum dsa_tag_protocol { 62 63 DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE, ··· 92 91 DSA_TAG_PROTO_VSC73XX_8021Q = DSA_TAG_PROTO_VSC73XX_8021Q_VALUE, 93 92 DSA_TAG_PROTO_YT921X = DSA_TAG_PROTO_YT921X_VALUE, 94 93 DSA_TAG_PROTO_MXL_GSW1XX = DSA_TAG_PROTO_MXL_GSW1XX_VALUE, 94 + DSA_TAG_PROTO_MXL862 = DSA_TAG_PROTO_MXL862_VALUE, 95 95 }; 96 96 97 97 struct dsa_switch;
+7
net/dsa/Kconfig
··· 104 104 Say Y or M if you want to enable support for tagging frames for 105 105 Mediatek switches. 106 106 107 + config NET_DSA_TAG_MXL_862XX 108 + tristate "Tag driver for MaxLinear MxL862xx switches" 109 + help 110 + Say Y or M if you want to enable support for tagging frames for the 111 + MaxLinear MxL86252 and MxL86282 switches using their native 8-byte 112 + tagging protocol. 113 + 107 114 config NET_DSA_TAG_MXL_GSW1XX 108 115 tristate "Tag driver for MaxLinear GSW1xx switches" 109 116 help
+1
net/dsa/Makefile
··· 28 28 obj-$(CONFIG_NET_DSA_TAG_KSZ) += tag_ksz.o 29 29 obj-$(CONFIG_NET_DSA_TAG_LAN9303) += tag_lan9303.o 30 30 obj-$(CONFIG_NET_DSA_TAG_MTK) += tag_mtk.o 31 + obj-$(CONFIG_NET_DSA_TAG_MXL_862XX) += tag_mxl862xx.o 31 32 obj-$(CONFIG_NET_DSA_TAG_MXL_GSW1XX) += tag_mxl-gsw1xx.o 32 33 obj-$(CONFIG_NET_DSA_TAG_NONE) += tag_none.o 33 34 obj-$(CONFIG_NET_DSA_TAG_OCELOT) += tag_ocelot.o
+110
net/dsa/tag_mxl862xx.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-or-later 2 + /* 3 + * DSA Special Tag for MaxLinear 862xx switch chips 4 + * 5 + * Copyright (C) 2025 Daniel Golle <daniel@makrotopia.org> 6 + * Copyright (C) 2024 MaxLinear Inc. 7 + */ 8 + 9 + #include <linux/bitops.h> 10 + #include <linux/etherdevice.h> 11 + #include <linux/skbuff.h> 12 + #include <net/dsa.h> 13 + #include "tag.h" 14 + 15 + #define MXL862_NAME "mxl862xx" 16 + 17 + #define MXL862_HEADER_LEN 8 18 + 19 + /* Word 0 -> EtherType */ 20 + 21 + /* Word 2 */ 22 + #define MXL862_SUBIF_ID GENMASK(4, 0) 23 + 24 + /* Word 3 */ 25 + #define MXL862_IGP_EGP GENMASK(3, 0) 26 + 27 + static struct sk_buff *mxl862_tag_xmit(struct sk_buff *skb, 28 + struct net_device *dev) 29 + { 30 + struct dsa_port *dp = dsa_user_to_port(dev); 31 + struct dsa_port *cpu_dp = dp->cpu_dp; 32 + unsigned int cpu_port, sub_interface; 33 + __be16 *mxl862_tag; 34 + 35 + cpu_port = cpu_dp->index; 36 + 37 + /* target port sub-interface ID relative to the CPU port */ 38 + sub_interface = dp->index + 16 - cpu_port; 39 + 40 + /* provide additional space 'MXL862_HEADER_LEN' bytes */ 41 + skb_push(skb, MXL862_HEADER_LEN); 42 + 43 + /* shift MAC address to the beginning of the enlarged buffer, 44 + * releasing the space required for DSA tag (between MAC address and 45 + * Ethertype) 46 + */ 47 + dsa_alloc_etype_header(skb, MXL862_HEADER_LEN); 48 + 49 + /* special tag ingress (from the perspective of the switch) */ 50 + mxl862_tag = dsa_etype_header_pos_tx(skb); 51 + mxl862_tag[0] = htons(ETH_P_MXLGSW); 52 + mxl862_tag[1] = 0; 53 + mxl862_tag[2] = htons(FIELD_PREP(MXL862_SUBIF_ID, sub_interface)); 54 + mxl862_tag[3] = htons(FIELD_PREP(MXL862_IGP_EGP, cpu_port)); 55 + 56 + return skb; 57 + } 58 + 59 + static struct sk_buff *mxl862_tag_rcv(struct sk_buff *skb, 60 + struct net_device *dev) 61 + { 62 + __be16 *mxl862_tag; 63 + int port; 64 + 65 + if (unlikely(!pskb_may_pull(skb, MXL862_HEADER_LEN))) { 66 + dev_warn_ratelimited(&dev->dev, "Cannot pull SKB, packet dropped\n"); 67 + return NULL; 68 + } 69 + 70 + mxl862_tag = dsa_etype_header_pos_rx(skb); 71 + 72 + if (unlikely(mxl862_tag[0] != htons(ETH_P_MXLGSW))) { 73 + dev_warn_ratelimited(&dev->dev, 74 + "Invalid special tag marker, packet dropped, tag: %8ph\n", 75 + mxl862_tag); 76 + return NULL; 77 + } 78 + 79 + /* Get source port information */ 80 + port = FIELD_GET(MXL862_IGP_EGP, ntohs(mxl862_tag[3])); 81 + skb->dev = dsa_conduit_find_user(dev, 0, port); 82 + if (unlikely(!skb->dev)) { 83 + dev_warn_ratelimited(&dev->dev, 84 + "Invalid source port, packet dropped, tag: %8ph\n", 85 + mxl862_tag); 86 + return NULL; 87 + } 88 + 89 + /* remove the MxL862xx special tag between the MAC addresses and the 90 + * current ethertype field. 91 + */ 92 + skb_pull_rcsum(skb, MXL862_HEADER_LEN); 93 + dsa_strip_etype_header(skb, MXL862_HEADER_LEN); 94 + 95 + return skb; 96 + } 97 + 98 + static const struct dsa_device_ops mxl862_netdev_ops = { 99 + .name = MXL862_NAME, 100 + .proto = DSA_TAG_PROTO_MXL862, 101 + .xmit = mxl862_tag_xmit, 102 + .rcv = mxl862_tag_rcv, 103 + .needed_headroom = MXL862_HEADER_LEN, 104 + }; 105 + 106 + MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_MXL862, MXL862_NAME); 107 + MODULE_DESCRIPTION("DSA tag driver for MaxLinear MxL862xx switches"); 108 + MODULE_LICENSE("GPL"); 109 + 110 + module_dsa_tag_driver(mxl862_netdev_ops);