diff --git a/MAINTAINERS b/MAINTAINERS index 0c9b34eda5ac..47882abf99dd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -15631,6 +15631,7 @@ M: Daniel Golle L: netdev@vger.kernel.org S: Maintained F: Documentation/devicetree/bindings/net/dsa/maxlinear,mxl862xx.yaml +F: drivers/net/dsa/mxl862xx/ F: net/dsa/tag_mxl862xx.c MCAN DEVICE DRIVER diff --git a/drivers/net/dsa/Kconfig b/drivers/net/dsa/Kconfig index 24c37cbf70d7..39fb8ead16b5 100644 --- a/drivers/net/dsa/Kconfig +++ b/drivers/net/dsa/Kconfig @@ -74,6 +74,8 @@ source "drivers/net/dsa/microchip/Kconfig" source "drivers/net/dsa/mv88e6xxx/Kconfig" +source "drivers/net/dsa/mxl862xx/Kconfig" + source "drivers/net/dsa/ocelot/Kconfig" source "drivers/net/dsa/qca/Kconfig" diff --git a/drivers/net/dsa/Makefile b/drivers/net/dsa/Makefile index 16de4ba3fa38..f5a463b87ec2 100644 --- a/drivers/net/dsa/Makefile +++ b/drivers/net/dsa/Makefile @@ -20,6 +20,7 @@ obj-y += hirschmann/ obj-y += lantiq/ obj-y += microchip/ obj-y += mv88e6xxx/ +obj-y += mxl862xx/ obj-y += ocelot/ obj-y += qca/ obj-y += realtek/ diff --git a/drivers/net/dsa/mxl862xx/Kconfig b/drivers/net/dsa/mxl862xx/Kconfig new file mode 100644 index 000000000000..4db7bab21a71 --- /dev/null +++ b/drivers/net/dsa/mxl862xx/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-only +config NET_DSA_MXL862 + tristate "MaxLinear MxL862xx" + depends on NET_DSA + select MAXLINEAR_GPHY + select NET_DSA_TAG_MXL_862XX + help + This enables support for the MaxLinear MxL862xx switch family. + These switches have two 10GE SerDes interfaces, one typically + used as CPU port. + - MxL86282 has eight 2.5 Gigabit PHYs + - MxL86252 has five 2.5 Gigabit PHYs diff --git a/drivers/net/dsa/mxl862xx/Makefile b/drivers/net/dsa/mxl862xx/Makefile new file mode 100644 index 000000000000..d23dd3cd511d --- /dev/null +++ b/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 mxl862xx-host.o diff --git a/drivers/net/dsa/mxl862xx/mxl862xx-api.h b/drivers/net/dsa/mxl862xx/mxl862xx-api.h new file mode 100644 index 000000000000..a9f599dbca25 --- /dev/null +++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h @@ -0,0 +1,675 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef __MXL862XX_API_H +#define __MXL862XX_API_H + +#include + +/** + * struct mdio_relay_data - relayed access to the switch internal MDIO bus + * @data: data to be read or written + * @phy: PHY index + * @mmd: MMD device + * @reg: register index + */ +struct mdio_relay_data { + __le16 data; + u8 phy; + u8 mmd; + __le16 reg; +} __packed; + +/** + * struct mxl862xx_register_mod - Register access parameter to directly + * modify internal registers + * @addr: Register address offset for modification + * @data: Value to write to the register address + * @mask: Mask of bits to be modified (1 to modify, 0 to ignore) + * + * Used for direct register modification operations. + */ +struct mxl862xx_register_mod { + __le16 addr; + __le16 data; + __le16 mask; +} __packed; + +/** + * enum mxl862xx_mac_clear_type - MAC table clear type + * @MXL862XX_MAC_CLEAR_PHY_PORT: clear dynamic entries based on port_id + * @MXL862XX_MAC_CLEAR_DYNAMIC: clear all dynamic entries + */ +enum mxl862xx_mac_clear_type { + MXL862XX_MAC_CLEAR_PHY_PORT = 0, + MXL862XX_MAC_CLEAR_DYNAMIC, +}; + +/** + * struct mxl862xx_mac_table_clear - MAC table clear + * @type: see &enum mxl862xx_mac_clear_type + * @port_id: physical port id + */ +struct mxl862xx_mac_table_clear { + u8 type; + u8 port_id; +} __packed; + +/** + * enum mxl862xx_age_timer - Aging Timer Value. + * @MXL862XX_AGETIMER_1_SEC: 1 second aging time + * @MXL862XX_AGETIMER_10_SEC: 10 seconds aging time + * @MXL862XX_AGETIMER_300_SEC: 300 seconds aging time + * @MXL862XX_AGETIMER_1_HOUR: 1 hour aging time + * @MXL862XX_AGETIMER_1_DAY: 24 hours aging time + * @MXL862XX_AGETIMER_CUSTOM: Custom aging time in seconds + */ +enum mxl862xx_age_timer { + MXL862XX_AGETIMER_1_SEC = 1, + MXL862XX_AGETIMER_10_SEC, + MXL862XX_AGETIMER_300_SEC, + MXL862XX_AGETIMER_1_HOUR, + MXL862XX_AGETIMER_1_DAY, + MXL862XX_AGETIMER_CUSTOM, +}; + +/** + * struct mxl862xx_bridge_alloc - Bridge Allocation + * @bridge_id: If the bridge allocation is successful, a valid ID will be + * returned in this field. Otherwise, INVALID_HANDLE is + * returned. For bridge free, this field should contain a + * valid ID returned by the bridge allocation. ID 0 is not + * used for historic reasons. + * + * Used by MXL862XX_BRIDGE_ALLOC and MXL862XX_BRIDGE_FREE. + */ +struct mxl862xx_bridge_alloc { + __le16 bridge_id; +}; + +/** + * enum mxl862xx_bridge_config_mask - Bridge configuration mask + * @MXL862XX_BRIDGE_CONFIG_MASK_MAC_LEARNING_LIMIT: + * Mask for mac_learning_limit_enable and mac_learning_limit. + * @MXL862XX_BRIDGE_CONFIG_MASK_MAC_LEARNED_COUNT: + * Mask for mac_learning_count + * @MXL862XX_BRIDGE_CONFIG_MASK_MAC_DISCARD_COUNT: + * Mask for learning_discard_event + * @MXL862XX_BRIDGE_CONFIG_MASK_SUB_METER: + * Mask for sub_metering_enable and traffic_sub_meter_id + * @MXL862XX_BRIDGE_CONFIG_MASK_FORWARDING_MODE: + * Mask for forward_broadcast, forward_unknown_multicast_ip, + * forward_unknown_multicast_non_ip and forward_unknown_unicast. + * @MXL862XX_BRIDGE_CONFIG_MASK_ALL: Enable all + * @MXL862XX_BRIDGE_CONFIG_MASK_FORCE: Bypass any check for debug purpose + */ +enum mxl862xx_bridge_config_mask { + MXL862XX_BRIDGE_CONFIG_MASK_MAC_LEARNING_LIMIT = BIT(0), + MXL862XX_BRIDGE_CONFIG_MASK_MAC_LEARNED_COUNT = BIT(1), + MXL862XX_BRIDGE_CONFIG_MASK_MAC_DISCARD_COUNT = BIT(2), + MXL862XX_BRIDGE_CONFIG_MASK_SUB_METER = BIT(3), + MXL862XX_BRIDGE_CONFIG_MASK_FORWARDING_MODE = BIT(4), + MXL862XX_BRIDGE_CONFIG_MASK_ALL = 0x7FFFFFFF, + MXL862XX_BRIDGE_CONFIG_MASK_FORCE = BIT(31) +}; + +/** + * enum mxl862xx_bridge_port_egress_meter - Meters for egress traffic type + * @MXL862XX_BRIDGE_PORT_EGRESS_METER_BROADCAST: + * Index of broadcast traffic meter + * @MXL862XX_BRIDGE_PORT_EGRESS_METER_MULTICAST: + * Index of known multicast traffic meter + * @MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_IP: + * Index of unknown multicast IP traffic meter + * @MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_NON_IP: + * Index of unknown multicast non-IP traffic meter + * @MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_UC: + * Index of unknown unicast traffic meter + * @MXL862XX_BRIDGE_PORT_EGRESS_METER_OTHERS: + * Index of traffic meter for other types + * @MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX: Number of index + */ +enum mxl862xx_bridge_port_egress_meter { + MXL862XX_BRIDGE_PORT_EGRESS_METER_BROADCAST = 0, + MXL862XX_BRIDGE_PORT_EGRESS_METER_MULTICAST, + MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_IP, + MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_MC_NON_IP, + MXL862XX_BRIDGE_PORT_EGRESS_METER_UNKNOWN_UC, + MXL862XX_BRIDGE_PORT_EGRESS_METER_OTHERS, + MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX, +}; + +/** + * enum mxl862xx_bridge_forward_mode - Bridge forwarding type of packet + * @MXL862XX_BRIDGE_FORWARD_FLOOD: Packet is flooded to port members of + * ingress bridge port + * @MXL862XX_BRIDGE_FORWARD_DISCARD: Packet is discarded + */ +enum mxl862xx_bridge_forward_mode { + MXL862XX_BRIDGE_FORWARD_FLOOD = 0, + MXL862XX_BRIDGE_FORWARD_DISCARD, +}; + +/** + * struct mxl862xx_bridge_config - Bridge Configuration + * @bridge_id: Bridge ID (FID) + * @mask: See &enum mxl862xx_bridge_config_mask + * @mac_learning_limit_enable: Enable MAC learning limitation + * @mac_learning_limit: Max number of MAC addresses that can be learned in + * this bridge (all bridge ports) + * @mac_learning_count: Number of MAC addresses learned from this bridge + * @learning_discard_event: Number of learning discard events due to + * hardware resource not available + * @sub_metering_enable: Traffic metering on type of traffic (such as + * broadcast, multicast, unknown unicast, etc) applies + * @traffic_sub_meter_id: Meter for bridge process with specific type (such + * as broadcast, multicast, unknown unicast, etc) + * @forward_broadcast: Forwarding mode of broadcast traffic. See + * &enum mxl862xx_bridge_forward_mode + * @forward_unknown_multicast_ip: Forwarding mode of unknown multicast IP + * traffic. + * See &enum mxl862xx_bridge_forward_mode + * @forward_unknown_multicast_non_ip: Forwarding mode of unknown multicast + * non-IP traffic. + * See &enum mxl862xx_bridge_forward_mode + * @forward_unknown_unicast: Forwarding mode of unknown unicast traffic. See + * &enum mxl862xx_bridge_forward_mode + */ +struct mxl862xx_bridge_config { + __le16 bridge_id; + __le32 mask; /* enum mxl862xx_bridge_config_mask */ + u8 mac_learning_limit_enable; + __le16 mac_learning_limit; + __le16 mac_learning_count; + __le32 learning_discard_event; + u8 sub_metering_enable[MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX]; + __le16 traffic_sub_meter_id[MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX]; + __le32 forward_broadcast; /* enum mxl862xx_bridge_forward_mode */ + __le32 forward_unknown_multicast_ip; /* enum mxl862xx_bridge_forward_mode */ + __le32 forward_unknown_multicast_non_ip; /* enum mxl862xx_bridge_forward_mode */ + __le32 forward_unknown_unicast; /* enum mxl862xx_bridge_forward_mode */ +} __packed; + +/** + * struct mxl862xx_bridge_port_alloc - Bridge Port Allocation + * @bridge_port_id: If the bridge port allocation is successful, a valid ID + * will be returned in this field. Otherwise, INVALID_HANDLE + * is returned. For bridge port free, this field should + * contain a valid ID returned by the bridge port allocation. + * + * Used by MXL862XX_BRIDGE_PORT_ALLOC and MXL862XX_BRIDGE_PORT_FREE. + */ +struct mxl862xx_bridge_port_alloc { + __le16 bridge_port_id; +}; + +/** + * enum mxl862xx_bridge_port_config_mask - Bridge Port configuration mask + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID: + * Mask for bridge_id + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN: + * Mask for ingress_extended_vlan_enable, + * ingress_extended_vlan_block_id and ingress_extended_vlan_block_size + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN: + * Mask for egress_extended_vlan_enable, egress_extended_vlan_block_id + * and egress_extended_vlan_block_size + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_MARKING: + * Mask for ingress_marking_mode + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_REMARKING: + * Mask for egress_remarking_mode + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_METER: + * Mask for ingress_metering_enable and ingress_traffic_meter_id + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_SUB_METER: + * Mask for egress_sub_metering_enable and egress_traffic_sub_meter_id + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_CTP_MAPPING: + * Mask for dest_logical_port_id, pmapper_enable, dest_sub_if_id_group, + * pmapper_mapping_mode, pmapper_id_valid and pmapper + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP: + * Mask for bridge_port_map + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_DEST_IP_LOOKUP: + * Mask for mc_dest_ip_lookup_disable + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_IP_LOOKUP: + * Mask for mc_src_ip_lookup_enable + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_DEST_MAC_LOOKUP: + * Mask for dest_mac_lookup_disable + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING: + * Mask for src_mac_learning_disable + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_MAC_SPOOFING: + * Mask for mac_spoofing_detect_enable + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_PORT_LOCK: + * Mask for port_lock_enable + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_MAC_LEARNING_LIMIT: + * Mask for mac_learning_limit_enable and mac_learning_limit + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_MAC_LEARNED_COUNT: + * Mask for mac_learning_count + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN_FILTER: + * Mask for ingress_vlan_filter_enable, ingress_vlan_filter_block_id + * and ingress_vlan_filter_block_size + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN_FILTER1: + * Mask for bypass_egress_vlan_filter1, egress_vlan_filter1enable, + * egress_vlan_filter1block_id and egress_vlan_filter1block_size + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN_FILTER2: + * Mask for egress_vlan_filter2enable, egress_vlan_filter2block_id and + * egress_vlan_filter2block_size + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MAC_LEARNING: + * Mask for vlan_tag_selection, vlan_src_mac_priority_enable, + * vlan_src_mac_dei_enable, vlan_src_mac_vid_enable, + * vlan_dst_mac_priority_enable, vlan_dst_mac_dei_enable and + * vlan_dst_mac_vid_enable + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MULTICAST_LOOKUP: + * Mask for vlan_multicast_priority_enable, + * vlan_multicast_dei_enable and vlan_multicast_vid_enable + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_LOOP_VIOLATION_COUNTER: + * Mask for loop_violation_count + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_ALL: Enable all + * @MXL862XX_BRIDGE_PORT_CONFIG_MASK_FORCE: Bypass any check for debug purpose + */ +enum mxl862xx_bridge_port_config_mask { + MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID = BIT(0), + MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN = BIT(1), + MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN = BIT(2), + MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_MARKING = BIT(3), + MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_REMARKING = BIT(4), + MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_METER = BIT(5), + MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_SUB_METER = BIT(6), + MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_CTP_MAPPING = BIT(7), + MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP = BIT(8), + MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_DEST_IP_LOOKUP = BIT(9), + MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_IP_LOOKUP = BIT(10), + MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_DEST_MAC_LOOKUP = BIT(11), + MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING = BIT(12), + MXL862XX_BRIDGE_PORT_CONFIG_MASK_MAC_SPOOFING = BIT(13), + MXL862XX_BRIDGE_PORT_CONFIG_MASK_PORT_LOCK = BIT(14), + MXL862XX_BRIDGE_PORT_CONFIG_MASK_MAC_LEARNING_LIMIT = BIT(15), + MXL862XX_BRIDGE_PORT_CONFIG_MASK_MAC_LEARNED_COUNT = BIT(16), + MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN_FILTER = BIT(17), + MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN_FILTER1 = BIT(18), + MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN_FILTER2 = BIT(19), + MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MAC_LEARNING = BIT(20), + MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MULTICAST_LOOKUP = BIT(21), + MXL862XX_BRIDGE_PORT_CONFIG_MASK_LOOP_VIOLATION_COUNTER = BIT(22), + MXL862XX_BRIDGE_PORT_CONFIG_MASK_ALL = 0x7FFFFFFF, + MXL862XX_BRIDGE_PORT_CONFIG_MASK_FORCE = BIT(31) +}; + +/** + * enum mxl862xx_color_marking_mode - Color Marking Mode + * @MXL862XX_MARKING_ALL_GREEN: mark packets (except critical) to green + * @MXL862XX_MARKING_INTERNAL_MARKING: do not change color and priority + * @MXL862XX_MARKING_DEI: DEI mark mode + * @MXL862XX_MARKING_PCP_8P0D: PCP 8P0D mark mode + * @MXL862XX_MARKING_PCP_7P1D: PCP 7P1D mark mode + * @MXL862XX_MARKING_PCP_6P2D: PCP 6P2D mark mode + * @MXL862XX_MARKING_PCP_5P3D: PCP 5P3D mark mode + * @MXL862XX_MARKING_DSCP_AF: DSCP AF class + */ +enum mxl862xx_color_marking_mode { + MXL862XX_MARKING_ALL_GREEN = 0, + MXL862XX_MARKING_INTERNAL_MARKING, + MXL862XX_MARKING_DEI, + MXL862XX_MARKING_PCP_8P0D, + MXL862XX_MARKING_PCP_7P1D, + MXL862XX_MARKING_PCP_6P2D, + MXL862XX_MARKING_PCP_5P3D, + MXL862XX_MARKING_DSCP_AF, +}; + +/** + * enum mxl862xx_color_remarking_mode - Color Remarking Mode + * @MXL862XX_REMARKING_NONE: values from last process stage + * @MXL862XX_REMARKING_DEI: DEI mark mode + * @MXL862XX_REMARKING_PCP_8P0D: PCP 8P0D mark mode + * @MXL862XX_REMARKING_PCP_7P1D: PCP 7P1D mark mode + * @MXL862XX_REMARKING_PCP_6P2D: PCP 6P2D mark mode + * @MXL862XX_REMARKING_PCP_5P3D: PCP 5P3D mark mode + * @MXL862XX_REMARKING_DSCP_AF: DSCP AF class + */ +enum mxl862xx_color_remarking_mode { + MXL862XX_REMARKING_NONE = 0, + MXL862XX_REMARKING_DEI = 2, + MXL862XX_REMARKING_PCP_8P0D, + MXL862XX_REMARKING_PCP_7P1D, + MXL862XX_REMARKING_PCP_6P2D, + MXL862XX_REMARKING_PCP_5P3D, + MXL862XX_REMARKING_DSCP_AF, +}; + +/** + * enum mxl862xx_pmapper_mapping_mode - P-mapper Mapping Mode + * @MXL862XX_PMAPPER_MAPPING_PCP: Use PCP for VLAN tagged packets to derive + * sub interface ID group + * @MXL862XX_PMAPPER_MAPPING_LAG: Use LAG Index for Pmapper access + * regardless of IP and VLAN packet + * @MXL862XX_PMAPPER_MAPPING_DSCP: Use DSCP for VLAN tagged IP packets to + * derive sub interface ID group + */ +enum mxl862xx_pmapper_mapping_mode { + MXL862XX_PMAPPER_MAPPING_PCP = 0, + MXL862XX_PMAPPER_MAPPING_LAG, + MXL862XX_PMAPPER_MAPPING_DSCP, +}; + +/** + * struct mxl862xx_pmapper - P-mapper Configuration + * @pmapper_id: Index of P-mapper (0-31) + * @dest_sub_if_id_group: Sub interface ID group. Entry 0 is for non-IP and + * non-VLAN tagged packets. + * Entries 1-8 are PCP mapping entries for VLAN tagged + * packets. + * Entries 9-72 are DSCP or LAG mapping entries. + * + * Used by CTP port config and bridge port config. 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. + */ +struct mxl862xx_pmapper { + __le16 pmapper_id; + u8 dest_sub_if_id_group[73]; +} __packed; + +/** + * struct mxl862xx_bridge_port_config - Bridge Port Configuration + * @bridge_port_id: Bridge Port ID allocated by bridge port allocation + * @mask: See &enum mxl862xx_bridge_port_config_mask + * @bridge_id: Bridge ID (FID) to which this bridge port is associated + * @ingress_extended_vlan_enable: Enable extended VLAN processing for + * ingress traffic + * @ingress_extended_vlan_block_id: Extended VLAN block allocated for + * ingress traffic + * @ingress_extended_vlan_block_size: Extended VLAN block size for ingress + * traffic + * @egress_extended_vlan_enable: Enable extended VLAN processing for egress + * traffic + * @egress_extended_vlan_block_id: Extended VLAN block allocated for egress + * traffic + * @egress_extended_vlan_block_size: Extended VLAN block size for egress + * traffic + * @ingress_marking_mode: Ingress color marking mode. See + * &enum mxl862xx_color_marking_mode + * @egress_remarking_mode: Color remarking for egress traffic. See + * &enum mxl862xx_color_remarking_mode + * @ingress_metering_enable: Traffic metering on ingress traffic applies + * @ingress_traffic_meter_id: Meter for ingress Bridge Port process + * @egress_sub_metering_enable: Traffic metering on various types of egress + * traffic + * @egress_traffic_sub_meter_id: Meter for egress Bridge Port process with + * specific type + * @dest_logical_port_id: Destination logical port + * @pmapper_enable: Enable P-mapper + * @dest_sub_if_id_group: Destination sub interface ID group when + * pmapper_enable is false + * @pmapper_mapping_mode: P-mapper mapping mode. See + * &enum mxl862xx_pmapper_mapping_mode + * @pmapper_id_valid: When true, P-mapper is re-used; when false, + * allocation is handled by API + * @pmapper: P-mapper configuration used when pmapper_enable is true + * @bridge_port_map: Port map defining broadcast domain. Each bit + * represents one bridge port. Bridge port ID is + * index * 16 + bit offset. + * @mc_dest_ip_lookup_disable: Disable multicast IP destination table + * lookup + * @mc_src_ip_lookup_enable: Enable multicast IP source table lookup + * @dest_mac_lookup_disable: Disable destination MAC lookup; packet treated + * as unknown + * @src_mac_learning_disable: Disable source MAC address learning + * @mac_spoofing_detect_enable: Enable MAC spoofing detection + * @port_lock_enable: Enable port locking + * @mac_learning_limit_enable: Enable MAC learning limitation + * @mac_learning_limit: Maximum number of MAC addresses that can be learned + * from this bridge port + * @loop_violation_count: Number of loop violation events from this bridge + * port + * @mac_learning_count: Number of MAC addresses learned from this bridge + * port + * @ingress_vlan_filter_enable: Enable ingress VLAN filter + * @ingress_vlan_filter_block_id: VLAN filter block of ingress traffic + * @ingress_vlan_filter_block_size: VLAN filter block size for ingress + * traffic + * @bypass_egress_vlan_filter1: For ingress traffic, bypass VLAN filter 1 + * at egress bridge port processing + * @egress_vlan_filter1enable: Enable egress VLAN filter 1 + * @egress_vlan_filter1block_id: VLAN filter block 1 of egress traffic + * @egress_vlan_filter1block_size: VLAN filter block 1 size + * @egress_vlan_filter2enable: Enable egress VLAN filter 2 + * @egress_vlan_filter2block_id: VLAN filter block 2 of egress traffic + * @egress_vlan_filter2block_size: VLAN filter block 2 size + * @vlan_tag_selection: VLAN tag selection for MAC address/multicast + * learning, lookup and filtering. + * 0 - Intermediate outer VLAN tag is used. + * 1 - Original outer VLAN tag is used. + * @vlan_src_mac_priority_enable: Enable VLAN Priority field for source MAC + * learning and filtering + * @vlan_src_mac_dei_enable: Enable VLAN DEI/CFI field for source MAC + * learning and filtering + * @vlan_src_mac_vid_enable: Enable VLAN ID field for source MAC learning + * and filtering + * @vlan_dst_mac_priority_enable: Enable VLAN Priority field for destination + * MAC lookup and filtering + * @vlan_dst_mac_dei_enable: Enable VLAN CFI/DEI field for destination MAC + * lookup and filtering + * @vlan_dst_mac_vid_enable: Enable VLAN ID field for destination MAC lookup + * and filtering + * @vlan_multicast_priority_enable: Enable VLAN Priority field for IP + * multicast lookup + * @vlan_multicast_dei_enable: Enable VLAN CFI/DEI field for IP multicast + * lookup + * @vlan_multicast_vid_enable: Enable VLAN ID field for IP multicast lookup + */ +struct mxl862xx_bridge_port_config { + __le16 bridge_port_id; + __le32 mask; /* enum mxl862xx_bridge_port_config_mask */ + __le16 bridge_id; + u8 ingress_extended_vlan_enable; + __le16 ingress_extended_vlan_block_id; + __le16 ingress_extended_vlan_block_size; + u8 egress_extended_vlan_enable; + __le16 egress_extended_vlan_block_id; + __le16 egress_extended_vlan_block_size; + __le32 ingress_marking_mode; /* enum mxl862xx_color_marking_mode */ + __le32 egress_remarking_mode; /* enum mxl862xx_color_remarking_mode */ + u8 ingress_metering_enable; + __le16 ingress_traffic_meter_id; + u8 egress_sub_metering_enable[MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX]; + __le16 egress_traffic_sub_meter_id[MXL862XX_BRIDGE_PORT_EGRESS_METER_MAX]; + u8 dest_logical_port_id; + u8 pmapper_enable; + __le16 dest_sub_if_id_group; + __le32 pmapper_mapping_mode; /* enum mxl862xx_pmapper_mapping_mode */ + u8 pmapper_id_valid; + struct mxl862xx_pmapper pmapper; + __le16 bridge_port_map[8]; + u8 mc_dest_ip_lookup_disable; + u8 mc_src_ip_lookup_enable; + u8 dest_mac_lookup_disable; + u8 src_mac_learning_disable; + u8 mac_spoofing_detect_enable; + u8 port_lock_enable; + u8 mac_learning_limit_enable; + __le16 mac_learning_limit; + __le16 loop_violation_count; + __le16 mac_learning_count; + u8 ingress_vlan_filter_enable; + __le16 ingress_vlan_filter_block_id; + __le16 ingress_vlan_filter_block_size; + u8 bypass_egress_vlan_filter1; + u8 egress_vlan_filter1enable; + __le16 egress_vlan_filter1block_id; + __le16 egress_vlan_filter1block_size; + u8 egress_vlan_filter2enable; + __le16 egress_vlan_filter2block_id; + __le16 egress_vlan_filter2block_size; + u8 vlan_tag_selection; + u8 vlan_src_mac_priority_enable; + u8 vlan_src_mac_dei_enable; + u8 vlan_src_mac_vid_enable; + u8 vlan_dst_mac_priority_enable; + u8 vlan_dst_mac_dei_enable; + u8 vlan_dst_mac_vid_enable; + u8 vlan_multicast_priority_enable; + u8 vlan_multicast_dei_enable; + u8 vlan_multicast_vid_enable; +} __packed; + +/** + * struct mxl862xx_cfg - Global Switch configuration Attributes + * @mac_table_age_timer: See &enum mxl862xx_age_timer + * @age_timer: Custom MAC table aging timer in seconds + * @max_packet_len: Maximum Ethernet packet length + * @learning_limit_action: Automatic MAC address table learning limitation + * consecutive action + * @mac_locking_action: Accept or discard MAC port locking violation + * packets + * @mac_spoofing_action: Accept or discard MAC spoofing and port MAC locking + * violation packets + * @pause_mac_mode_src: Pause frame MAC source address mode + * @pause_mac_src: Pause frame MAC source address + */ +struct mxl862xx_cfg { + __le32 mac_table_age_timer; /* enum mxl862xx_age_timer */ + __le32 age_timer; + __le16 max_packet_len; + u8 learning_limit_action; + u8 mac_locking_action; + u8 mac_spoofing_action; + u8 pause_mac_mode_src; + u8 pause_mac_src[ETH_ALEN]; +} __packed; + +/** + * enum mxl862xx_ss_sp_tag_mask - Special tag valid field indicator bits + * @MXL862XX_SS_SP_TAG_MASK_RX: valid RX special tag mode + * @MXL862XX_SS_SP_TAG_MASK_TX: valid TX special tag mode + * @MXL862XX_SS_SP_TAG_MASK_RX_PEN: valid RX special tag info over preamble + * @MXL862XX_SS_SP_TAG_MASK_TX_PEN: valid TX special tag info over preamble + */ +enum mxl862xx_ss_sp_tag_mask { + MXL862XX_SS_SP_TAG_MASK_RX = BIT(0), + MXL862XX_SS_SP_TAG_MASK_TX = BIT(1), + MXL862XX_SS_SP_TAG_MASK_RX_PEN = BIT(2), + MXL862XX_SS_SP_TAG_MASK_TX_PEN = BIT(3), +}; + +/** + * enum mxl862xx_ss_sp_tag_rx - RX special tag mode + * @MXL862XX_SS_SP_TAG_RX_NO_TAG_NO_INSERT: packet does NOT have special + * tag and special tag is NOT inserted + * @MXL862XX_SS_SP_TAG_RX_NO_TAG_INSERT: packet does NOT have special tag + * and special tag is inserted + * @MXL862XX_SS_SP_TAG_RX_TAG_NO_INSERT: packet has special tag and special + * tag is NOT inserted + */ +enum mxl862xx_ss_sp_tag_rx { + MXL862XX_SS_SP_TAG_RX_NO_TAG_NO_INSERT = 0, + MXL862XX_SS_SP_TAG_RX_NO_TAG_INSERT = 1, + MXL862XX_SS_SP_TAG_RX_TAG_NO_INSERT = 2, +}; + +/** + * enum mxl862xx_ss_sp_tag_tx - TX special tag mode + * @MXL862XX_SS_SP_TAG_TX_NO_TAG_NO_REMOVE: packet does NOT have special + * tag and special tag is NOT removed + * @MXL862XX_SS_SP_TAG_TX_TAG_REPLACE: packet has special tag and special + * tag is replaced + * @MXL862XX_SS_SP_TAG_TX_TAG_NO_REMOVE: packet has special tag and special + * tag is NOT removed + * @MXL862XX_SS_SP_TAG_TX_TAG_REMOVE: packet has special tag and special + * tag is removed + */ +enum mxl862xx_ss_sp_tag_tx { + MXL862XX_SS_SP_TAG_TX_NO_TAG_NO_REMOVE = 0, + MXL862XX_SS_SP_TAG_TX_TAG_REPLACE = 1, + MXL862XX_SS_SP_TAG_TX_TAG_NO_REMOVE = 2, + MXL862XX_SS_SP_TAG_TX_TAG_REMOVE = 3, +}; + +/** + * enum mxl862xx_ss_sp_tag_rx_pen - RX special tag info over preamble + * @MXL862XX_SS_SP_TAG_RX_PEN_ALL_0: special tag info inserted from byte 2 + * to 7 are all 0 + * @MXL862XX_SS_SP_TAG_RX_PEN_BYTE_5_IS_16: special tag byte 5 is 16, other + * bytes from 2 to 7 are 0 + * @MXL862XX_SS_SP_TAG_RX_PEN_BYTE_5_FROM_PREAMBLE: special tag byte 5 is + * from preamble field, others + * are 0 + * @MXL862XX_SS_SP_TAG_RX_PEN_BYTE_2_TO_7_FROM_PREAMBLE: special tag byte 2 + * to 7 are from preamble + * field + */ +enum mxl862xx_ss_sp_tag_rx_pen { + MXL862XX_SS_SP_TAG_RX_PEN_ALL_0 = 0, + MXL862XX_SS_SP_TAG_RX_PEN_BYTE_5_IS_16 = 1, + MXL862XX_SS_SP_TAG_RX_PEN_BYTE_5_FROM_PREAMBLE = 2, + MXL862XX_SS_SP_TAG_RX_PEN_BYTE_2_TO_7_FROM_PREAMBLE = 3, +}; + +/** + * struct mxl862xx_ss_sp_tag - Special tag port settings + * @pid: port ID (1~16) + * @mask: See &enum mxl862xx_ss_sp_tag_mask + * @rx: See &enum mxl862xx_ss_sp_tag_rx + * @tx: See &enum mxl862xx_ss_sp_tag_tx + * @rx_pen: See &enum mxl862xx_ss_sp_tag_rx_pen + * @tx_pen: TX special tag info over preamble + * 0 - disabled + * 1 - enabled + */ +struct mxl862xx_ss_sp_tag { + u8 pid; + u8 mask; /* enum mxl862xx_ss_sp_tag_mask */ + u8 rx; /* enum mxl862xx_ss_sp_tag_rx */ + u8 tx; /* enum mxl862xx_ss_sp_tag_tx */ + u8 rx_pen; /* enum mxl862xx_ss_sp_tag_rx_pen */ + u8 tx_pen; /* boolean */ +} __packed; + +/** + * enum mxl862xx_logical_port_mode - Logical port mode + * @MXL862XX_LOGICAL_PORT_8BIT_WLAN: WLAN with 8-bit station ID + * @MXL862XX_LOGICAL_PORT_9BIT_WLAN: WLAN with 9-bit station ID + * @MXL862XX_LOGICAL_PORT_ETHERNET: Ethernet port + * @MXL862XX_LOGICAL_PORT_OTHER: Others + */ +enum mxl862xx_logical_port_mode { + MXL862XX_LOGICAL_PORT_8BIT_WLAN = 0, + MXL862XX_LOGICAL_PORT_9BIT_WLAN, + MXL862XX_LOGICAL_PORT_ETHERNET, + MXL862XX_LOGICAL_PORT_OTHER = 0xFF, +}; + +/** + * struct mxl862xx_ctp_port_assignment - CTP Port Assignment/association + * with logical port + * @logical_port_id: Logical Port Id. The valid range is hardware dependent + * @first_ctp_port_id: First CTP (Connectivity Termination Port) ID mapped + * to above logical port ID + * @number_of_ctp_port: Total number of CTP Ports mapped above logical port + * ID + * @mode: Logical port mode to define sub interface ID format. See + * &enum mxl862xx_logical_port_mode + * @bridge_port_id: Bridge Port ID (not FID). For allocation, each CTP + * allocated is mapped to the Bridge Port given by this field. + * The Bridge Port will be configured to use first CTP as + * egress CTP. + */ +struct mxl862xx_ctp_port_assignment { + u8 logical_port_id; + __le16 first_ctp_port_id; + __le16 number_of_ctp_port; + __le32 mode; /* enum mxl862xx_logical_port_mode */ + __le16 bridge_port_id; +} __packed; + +/** + * struct mxl862xx_sys_fw_image_version - Firmware version information + * @iv_major: firmware major version + * @iv_minor: firmware minor version + * @iv_revision: firmware revision + * @iv_build_num: firmware build number + */ +struct mxl862xx_sys_fw_image_version { + u8 iv_major; + u8 iv_minor; + __le16 iv_revision; + __le32 iv_build_num; +} __packed; + +#endif /* __MXL862XX_API_H */ diff --git a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h new file mode 100644 index 000000000000..f6852ade64e7 --- /dev/null +++ b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef __MXL862XX_CMD_H +#define __MXL862XX_CMD_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) + +#define MXL862XX_COMMON_MAGIC 0x100 +#define MXL862XX_BRDG_MAGIC 0x300 +#define MXL862XX_BRDGPORT_MAGIC 0x400 +#define MXL862XX_CTP_MAGIC 0x500 +#define MXL862XX_SWMAC_MAGIC 0xa00 +#define MXL862XX_SS_MAGIC 0x1600 +#define GPY_GPY2XX_MAGIC 0x1800 +#define SYS_MISC_MAGIC 0x1900 + +#define MXL862XX_COMMON_CFGGET (MXL862XX_COMMON_MAGIC + 0x9) +#define MXL862XX_COMMON_REGISTERMOD (MXL862XX_COMMON_MAGIC + 0x11) + +#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_PORTASSIGNMENTSET (MXL862XX_CTP_MAGIC + 0x3) + +#define MXL862XX_MAC_TABLECLEARCOND (MXL862XX_SWMAC_MAGIC + 0x8) + +#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 SYS_MISC_FW_VERSION (SYS_MISC_MAGIC + 0x02) + +#define MMD_API_MAXIMUM_ID 0x7fff + +#endif /* __MXL862XX_CMD_H */ diff --git a/drivers/net/dsa/mxl862xx/mxl862xx-host.c b/drivers/net/dsa/mxl862xx/mxl862xx-host.c new file mode 100644 index 000000000000..8c55497a0ce8 --- /dev/null +++ b/drivers/net/dsa/mxl862xx/mxl862xx-host.c @@ -0,0 +1,245 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Based upon the MaxLinear SDK driver + * + * Copyright (C) 2025 Daniel Golle + * Copyright (C) 2025 John Crispin + * Copyright (C) 2024 MaxLinear Inc. + */ + +#include +#include +#include +#include +#include "mxl862xx.h" +#include "mxl862xx-host.h" + +#define CTRL_BUSY_MASK BIT(15) + +#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) + +#define MMD_API_SET_DATA_0 2 +#define MMD_API_GET_DATA_0 5 +#define MMD_API_RST_DATA 8 + +#define MXL862XX_SWITCH_RESET 0x9907 + +static int mxl862xx_reg_read(struct mxl862xx_priv *priv, u32 addr) +{ + return __mdiodev_c45_read(priv->mdiodev, MDIO_MMD_VEND1, addr); +} + +static int mxl862xx_reg_write(struct mxl862xx_priv *priv, u32 addr, u16 data) +{ + return __mdiodev_c45_write(priv->mdiodev, MDIO_MMD_VEND1, addr, data); +} + +static int mxl862xx_ctrl_read(struct mxl862xx_priv *priv) +{ + return mxl862xx_reg_read(priv, MXL862XX_MMD_REG_CTRL); +} + +static int mxl862xx_busy_wait(struct mxl862xx_priv *priv) +{ + int val; + + return readx_poll_timeout(mxl862xx_ctrl_read, priv, val, + !(val & CTRL_BUSY_MASK), 15, 500000); +} + +static int mxl862xx_set_data(struct mxl862xx_priv *priv, u16 words) +{ + int ret; + u16 cmd; + + ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, + MXL862XX_MMD_REG_DATA_MAX_SIZE * sizeof(u16)); + 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_reg_write(priv, MXL862XX_MMD_REG_CTRL, + cmd | CTRL_BUSY_MASK); + if (ret < 0) + return ret; + + return mxl862xx_busy_wait(priv); +} + +static int mxl862xx_get_data(struct mxl862xx_priv *priv, u16 words) +{ + int ret; + u16 cmd; + + ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, + MXL862XX_MMD_REG_DATA_MAX_SIZE * sizeof(u16)); + 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_reg_write(priv, MXL862XX_MMD_REG_CTRL, + cmd | CTRL_BUSY_MASK); + if (ret < 0) + return ret; + + return mxl862xx_busy_wait(priv); +} + +static int mxl862xx_firmware_return(int ret) +{ + /* Only 16-bit values are valid. */ + if (WARN_ON(ret & GENMASK(31, 16))) + return -EINVAL; + + /* Interpret value as signed 16-bit integer. */ + return (s16)ret; +} + +static int mxl862xx_send_cmd(struct mxl862xx_priv *priv, u16 cmd, u16 size, + bool quiet) +{ + int ret; + + ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, size); + if (ret) + return ret; + + ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL, + cmd | CTRL_BUSY_MASK); + if (ret) + return ret; + + ret = mxl862xx_busy_wait(priv); + if (ret) + return ret; + + ret = mxl862xx_reg_read(priv, MXL862XX_MMD_REG_LEN_RET); + if (ret < 0) + return ret; + + /* handle errors returned by the firmware as -EIO + * The firmware is based on Zephyr OS and uses the errors as + * defined in errno.h of Zephyr OS. See + * https://github.com/zephyrproject-rtos/zephyr/blob/v3.7.0/lib/libc/minimal/include/errno.h + */ + ret = mxl862xx_firmware_return(ret); + if (ret < 0) { + if (!quiet) + dev_err(&priv->mdiodev->dev, + "CMD %04x returned error %d\n", cmd, ret); + return -EIO; + } + + return ret; +} + +int mxl862xx_api_wrap(struct mxl862xx_priv *priv, u16 cmd, void *_data, + u16 size, bool read, bool quiet) +{ + __le16 *data = _data; + int ret, cmd_ret; + u16 max, i; + + dev_dbg(&priv->mdiodev->dev, "CMD %04x DATA %*ph\n", cmd, size, data); + + mutex_lock_nested(&priv->mdiodev->bus->mdio_lock, MDIO_MUTEX_NESTED); + + max = (size + 1) / 2; + + ret = mxl862xx_busy_wait(priv); + if (ret < 0) + goto out; + + for (i = 0; i < max; i++) { + u16 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(priv, i); + if (ret < 0) + goto out; + } + + ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_DATA_FIRST + off, + le16_to_cpu(data[i])); + if (ret < 0) + goto out; + } + + ret = mxl862xx_send_cmd(priv, cmd, size, quiet); + if (ret < 0 || !read) + goto out; + + /* store result of mxl862xx_send_cmd() */ + cmd_ret = ret; + + for (i = 0; i < max; i++) { + u16 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(priv, i); + if (ret < 0) + goto out; + } + + ret = mxl862xx_reg_read(priv, MXL862XX_MMD_REG_DATA_FIRST + off); + if (ret < 0) + goto out; + + if ((i * 2 + 1) == size) { + /* Special handling for last BYTE if it's not WORD + * aligned to avoid writing beyond the allocated data + * structure. + */ + *(uint8_t *)&data[i] = ret & 0xff; + } else { + data[i] = cpu_to_le16((u16)ret); + } + } + + /* on success return the result of the mxl862xx_send_cmd() */ + ret = cmd_ret; + + dev_dbg(&priv->mdiodev->dev, "RET %d DATA %*ph\n", ret, size, data); + +out: + mutex_unlock(&priv->mdiodev->bus->mdio_lock); + + return ret; +} + +int mxl862xx_reset(struct mxl862xx_priv *priv) +{ + int ret; + + mutex_lock_nested(&priv->mdiodev->bus->mdio_lock, MDIO_MUTEX_NESTED); + + /* Software reset */ + ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_LEN_RET, 0); + if (ret) + goto out; + + ret = mxl862xx_reg_write(priv, MXL862XX_MMD_REG_CTRL, MXL862XX_SWITCH_RESET); +out: + mutex_unlock(&priv->mdiodev->bus->mdio_lock); + + return ret; +} diff --git a/drivers/net/dsa/mxl862xx/mxl862xx-host.h b/drivers/net/dsa/mxl862xx/mxl862xx-host.h new file mode 100644 index 000000000000..7cc496f6be5c --- /dev/null +++ b/drivers/net/dsa/mxl862xx/mxl862xx-host.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef __MXL862XX_HOST_H +#define __MXL862XX_HOST_H + +#include "mxl862xx.h" + +int mxl862xx_api_wrap(struct mxl862xx_priv *priv, u16 cmd, void *data, u16 size, + bool read, bool quiet); +int mxl862xx_reset(struct mxl862xx_priv *priv); + +#endif /* __MXL862XX_HOST_H */ diff --git a/drivers/net/dsa/mxl862xx/mxl862xx.c b/drivers/net/dsa/mxl862xx/mxl862xx.c new file mode 100644 index 000000000000..b1e2094b5816 --- /dev/null +++ b/drivers/net/dsa/mxl862xx/mxl862xx.c @@ -0,0 +1,476 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Driver for MaxLinear MxL862xx switch family + * + * Copyright (C) 2024 MaxLinear Inc. + * Copyright (C) 2025 John Crispin + * Copyright (C) 2025 Daniel Golle + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "mxl862xx.h" +#include "mxl862xx-api.h" +#include "mxl862xx-cmd.h" +#include "mxl862xx-host.h" + +#define MXL862XX_API_WRITE(dev, cmd, data) \ + mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), false, false) +#define MXL862XX_API_READ(dev, cmd, data) \ + mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), true, false) +#define MXL862XX_API_READ_QUIET(dev, cmd, data) \ + mxl862xx_api_wrap(dev, cmd, &(data), sizeof((data)), true, true) + +#define MXL862XX_SDMA_PCTRLP(p) (0xbc0 + ((p) * 0x6)) +#define MXL862XX_SDMA_PCTRL_EN BIT(0) + +#define MXL862XX_FDMA_PCTRLP(p) (0xa80 + ((p) * 0x6)) +#define MXL862XX_FDMA_PCTRL_EN BIT(0) + +#define MXL862XX_READY_TIMEOUT_MS 10000 +#define MXL862XX_READY_POLL_MS 100 + +static enum dsa_tag_protocol mxl862xx_get_tag_protocol(struct dsa_switch *ds, + int port, + enum dsa_tag_protocol m) +{ + return DSA_TAG_PROTO_MXL862; +} + +/* PHY access via firmware relay */ +static int mxl862xx_phy_read_mmd(struct mxl862xx_priv *priv, int port, + int devadd, int reg) +{ + struct mdio_relay_data param = { + .phy = port, + .mmd = devadd, + .reg = cpu_to_le16(reg), + }; + int ret; + + ret = MXL862XX_API_READ(priv, INT_GPHY_READ, param); + if (ret) + return ret; + + return le16_to_cpu(param.data); +} + +static int mxl862xx_phy_write_mmd(struct mxl862xx_priv *priv, int port, + int devadd, int reg, u16 data) +{ + struct mdio_relay_data param = { + .phy = port, + .mmd = devadd, + .reg = cpu_to_le16(reg), + .data = cpu_to_le16(data), + }; + + return MXL862XX_API_WRITE(priv, INT_GPHY_WRITE, param); +} + +static int mxl862xx_phy_read_mii_bus(struct mii_bus *bus, int port, int regnum) +{ + return mxl862xx_phy_read_mmd(bus->priv, port, 0, regnum); +} + +static int mxl862xx_phy_write_mii_bus(struct mii_bus *bus, int port, + int regnum, u16 val) +{ + return mxl862xx_phy_write_mmd(bus->priv, port, 0, regnum, val); +} + +static int mxl862xx_phy_read_c45_mii_bus(struct mii_bus *bus, int port, + int devadd, int regnum) +{ + return mxl862xx_phy_read_mmd(bus->priv, port, devadd, regnum); +} + +static int mxl862xx_phy_write_c45_mii_bus(struct mii_bus *bus, int port, + int devadd, int regnum, u16 val) +{ + return mxl862xx_phy_write_mmd(bus->priv, port, devadd, regnum, val); +} + +static int mxl862xx_wait_ready(struct dsa_switch *ds) +{ + struct mxl862xx_sys_fw_image_version ver = {}; + unsigned long start = jiffies, timeout; + struct mxl862xx_priv *priv = ds->priv; + struct mxl862xx_cfg cfg = {}; + int ret; + + timeout = start + msecs_to_jiffies(MXL862XX_READY_TIMEOUT_MS); + msleep(2000); /* it always takes at least 2 seconds */ + do { + ret = MXL862XX_API_READ_QUIET(priv, SYS_MISC_FW_VERSION, ver); + if (ret || !ver.iv_major) + goto not_ready_yet; + + /* being able to perform CFGGET indicates that + * the firmware is ready + */ + ret = MXL862XX_API_READ_QUIET(priv, + MXL862XX_COMMON_CFGGET, + cfg); + if (ret) + goto not_ready_yet; + + dev_info(ds->dev, "switch ready after %ums, firmware %u.%u.%u (build %u)\n", + jiffies_to_msecs(jiffies - start), + ver.iv_major, ver.iv_minor, + le16_to_cpu(ver.iv_revision), + le32_to_cpu(ver.iv_build_num)); + return 0; + +not_ready_yet: + msleep(MXL862XX_READY_POLL_MS); + } while (time_before(jiffies, timeout)); + + dev_err(ds->dev, "switch not responding after reset\n"); + return -ETIMEDOUT; +} + +static int mxl862xx_setup_mdio(struct dsa_switch *ds) +{ + struct mxl862xx_priv *priv = ds->priv; + struct device *dev = ds->dev; + struct device_node *mdio_np; + struct mii_bus *bus; + int ret; + + bus = devm_mdiobus_alloc(dev); + if (!bus) + return -ENOMEM; + + bus->priv = priv; + ds->user_mii_bus = bus; + bus->name = KBUILD_MODNAME "-mii"; + snprintf(bus->id, MII_BUS_ID_SIZE, "%s-mii", dev_name(dev)); + 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; + + mdio_np = of_get_child_by_name(dev->of_node, "mdio"); + if (!mdio_np) + return -ENODEV; + + ret = devm_of_mdiobus_register(dev, bus, mdio_np); + of_node_put(mdio_np); + + return ret; +} + +static int mxl862xx_setup(struct dsa_switch *ds) +{ + struct mxl862xx_priv *priv = ds->priv; + int ret; + + ret = mxl862xx_reset(priv); + if (ret) + return ret; + + ret = mxl862xx_wait_ready(ds); + if (ret) + return ret; + + return mxl862xx_setup_mdio(ds); +} + +static int mxl862xx_port_state(struct dsa_switch *ds, int port, bool enable) +{ + struct mxl862xx_register_mod sdma = { + .addr = cpu_to_le16(MXL862XX_SDMA_PCTRLP(port)), + .data = cpu_to_le16(enable ? MXL862XX_SDMA_PCTRL_EN : 0), + .mask = cpu_to_le16(MXL862XX_SDMA_PCTRL_EN), + }; + struct mxl862xx_register_mod fdma = { + .addr = cpu_to_le16(MXL862XX_FDMA_PCTRLP(port)), + .data = cpu_to_le16(enable ? MXL862XX_FDMA_PCTRL_EN : 0), + .mask = cpu_to_le16(MXL862XX_FDMA_PCTRL_EN), + }; + int ret; + + ret = MXL862XX_API_WRITE(ds->priv, MXL862XX_COMMON_REGISTERMOD, sdma); + if (ret) + return ret; + + return MXL862XX_API_WRITE(ds->priv, MXL862XX_COMMON_REGISTERMOD, fdma); +} + +static int mxl862xx_port_enable(struct dsa_switch *ds, int port, + struct phy_device *phydev) +{ + return mxl862xx_port_state(ds, port, true); +} + +static void mxl862xx_port_disable(struct dsa_switch *ds, int port) +{ + if (mxl862xx_port_state(ds, port, false)) + dev_err(ds->dev, "failed to disable port %d\n", port); +} + +static void mxl862xx_port_fast_age(struct dsa_switch *ds, int port) +{ + struct mxl862xx_mac_table_clear param = { + .type = MXL862XX_MAC_CLEAR_PHY_PORT, + .port_id = port, + }; + + if (MXL862XX_API_WRITE(ds->priv, MXL862XX_MAC_TABLECLEARCOND, param)) + dev_err(ds->dev, "failed to clear fdb on port %d\n", port); +} + +static int mxl862xx_configure_ctp_port(struct dsa_switch *ds, int port, + u16 first_ctp_port_id, + u16 number_of_ctp_ports) +{ + struct mxl862xx_ctp_port_assignment ctp_assign = { + .logical_port_id = port, + .first_ctp_port_id = cpu_to_le16(first_ctp_port_id), + .number_of_ctp_port = cpu_to_le16(number_of_ctp_ports), + .mode = cpu_to_le32(MXL862XX_LOGICAL_PORT_ETHERNET), + }; + + return MXL862XX_API_WRITE(ds->priv, MXL862XX_CTP_PORTASSIGNMENTSET, + ctp_assign); +} + +static int mxl862xx_configure_sp_tag_proto(struct dsa_switch *ds, int port, + bool enable) +{ + struct mxl862xx_ss_sp_tag tag = { + .pid = port, + .mask = MXL862XX_SS_SP_TAG_MASK_RX | MXL862XX_SS_SP_TAG_MASK_TX, + .rx = enable ? MXL862XX_SS_SP_TAG_RX_TAG_NO_INSERT : + MXL862XX_SS_SP_TAG_RX_NO_TAG_INSERT, + .tx = enable ? MXL862XX_SS_SP_TAG_TX_TAG_NO_REMOVE : + MXL862XX_SS_SP_TAG_TX_TAG_REMOVE, + }; + + return MXL862XX_API_WRITE(ds->priv, MXL862XX_SS_SPTAG_SET, tag); +} + +static int mxl862xx_setup_cpu_bridge(struct dsa_switch *ds, int port) +{ + struct mxl862xx_bridge_port_config br_port_cfg = {}; + struct mxl862xx_priv *priv = ds->priv; + u16 bridge_port_map = 0; + struct dsa_port *dp; + + /* CPU port bridge setup */ + br_port_cfg.mask = cpu_to_le32(MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP | + MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING | + MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MAC_LEARNING); + + br_port_cfg.bridge_port_id = cpu_to_le16(port); + br_port_cfg.src_mac_learning_disable = false; + br_port_cfg.vlan_src_mac_vid_enable = true; + br_port_cfg.vlan_dst_mac_vid_enable = true; + + /* include all assigned user ports in the CPU portmap */ + dsa_switch_for_each_user_port(dp, ds) { + /* it's safe to rely on cpu_dp being valid for user ports */ + if (dp->cpu_dp->index != port) + continue; + + bridge_port_map |= BIT(dp->index); + } + br_port_cfg.bridge_port_map[0] |= cpu_to_le16(bridge_port_map); + + return MXL862XX_API_WRITE(priv, MXL862XX_BRIDGEPORT_CONFIGSET, br_port_cfg); +} + +static int mxl862xx_add_single_port_bridge(struct dsa_switch *ds, int port) +{ + struct mxl862xx_bridge_port_config br_port_cfg = {}; + struct dsa_port *dp = dsa_to_port(ds, port); + struct mxl862xx_bridge_alloc br_alloc = {}; + int ret; + + ret = MXL862XX_API_READ(ds->priv, MXL862XX_BRIDGE_ALLOC, br_alloc); + if (ret) { + dev_err(ds->dev, "failed to allocate a bridge for port %d\n", port); + return ret; + } + + br_port_cfg.bridge_id = br_alloc.bridge_id; + br_port_cfg.bridge_port_id = cpu_to_le16(port); + br_port_cfg.mask = cpu_to_le32(MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID | + MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP | + MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING | + MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MAC_LEARNING); + br_port_cfg.src_mac_learning_disable = true; + br_port_cfg.vlan_src_mac_vid_enable = false; + br_port_cfg.vlan_dst_mac_vid_enable = false; + /* As this function is only called for user ports it is safe to rely on + * cpu_dp being valid + */ + br_port_cfg.bridge_port_map[0] = cpu_to_le16(BIT(dp->cpu_dp->index)); + + return MXL862XX_API_WRITE(ds->priv, MXL862XX_BRIDGEPORT_CONFIGSET, br_port_cfg); +} + +static int mxl862xx_port_setup(struct dsa_switch *ds, int port) +{ + struct dsa_port *dp = dsa_to_port(ds, port); + bool is_cpu_port = dsa_port_is_cpu(dp); + int ret; + + /* disable port and flush MAC entries */ + ret = mxl862xx_port_state(ds, port, false); + if (ret) + return ret; + + mxl862xx_port_fast_age(ds, port); + + /* skip setup for unused and DSA ports */ + if (dsa_port_is_unused(dp) || + dsa_port_is_dsa(dp)) + return 0; + + /* configure tag protocol */ + ret = mxl862xx_configure_sp_tag_proto(ds, port, is_cpu_port); + if (ret) + return ret; + + /* assign CTP port IDs */ + ret = mxl862xx_configure_ctp_port(ds, port, port, + is_cpu_port ? 32 - port : 1); + if (ret) + return ret; + + if (is_cpu_port) + /* assign user ports to CPU port bridge */ + return mxl862xx_setup_cpu_bridge(ds, port); + + /* setup single-port bridge for user ports */ + return mxl862xx_add_single_port_bridge(ds, port); +} + +static void mxl862xx_phylink_get_caps(struct dsa_switch *ds, int port, + struct phylink_config *config) +{ + config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | MAC_10 | + MAC_100 | MAC_1000 | MAC_2500FD; + + __set_bit(PHY_INTERFACE_MODE_INTERNAL, + config->supported_interfaces); +} + +static const struct dsa_switch_ops mxl862xx_switch_ops = { + .get_tag_protocol = mxl862xx_get_tag_protocol, + .setup = mxl862xx_setup, + .port_setup = mxl862xx_port_setup, + .phylink_get_caps = mxl862xx_phylink_get_caps, + .port_enable = mxl862xx_port_enable, + .port_disable = mxl862xx_port_disable, + .port_fast_age = mxl862xx_port_fast_age, +}; + +static void mxl862xx_phylink_mac_config(struct phylink_config *config, + unsigned int mode, + const struct phylink_link_state *state) +{ +} + +static void mxl862xx_phylink_mac_link_down(struct phylink_config *config, + unsigned int mode, + phy_interface_t interface) +{ +} + +static void mxl862xx_phylink_mac_link_up(struct phylink_config *config, + struct phy_device *phydev, + unsigned int mode, + phy_interface_t interface, + int speed, int duplex, + bool tx_pause, bool rx_pause) +{ +} + +static const struct phylink_mac_ops mxl862xx_phylink_mac_ops = { + .mac_config = mxl862xx_phylink_mac_config, + .mac_link_down = mxl862xx_phylink_mac_link_down, + .mac_link_up = mxl862xx_phylink_mac_link_up, +}; + +static int mxl862xx_probe(struct mdio_device *mdiodev) +{ + struct device *dev = &mdiodev->dev; + struct mxl862xx_priv *priv; + struct dsa_switch *ds; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->mdiodev = mdiodev; + + ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL); + if (!ds) + return -ENOMEM; + + priv->ds = ds; + ds->dev = dev; + ds->priv = priv; + ds->ops = &mxl862xx_switch_ops; + ds->phylink_mac_ops = &mxl862xx_phylink_mac_ops; + ds->num_ports = MXL862XX_MAX_PORTS; + + dev_set_drvdata(dev, ds); + + return dsa_register_switch(ds); +} + +static void mxl862xx_remove(struct mdio_device *mdiodev) +{ + struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev); + + if (!ds) + return; + + dsa_unregister_switch(ds); +} + +static void mxl862xx_shutdown(struct mdio_device *mdiodev) +{ + struct dsa_switch *ds = dev_get_drvdata(&mdiodev->dev); + + if (!ds) + return; + + dsa_switch_shutdown(ds); + + dev_set_drvdata(&mdiodev->dev, NULL); +} + +static const struct of_device_id mxl862xx_of_match[] = { + { .compatible = "maxlinear,mxl86282" }, + { .compatible = "maxlinear,mxl86252" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, mxl862xx_of_match); + +static struct mdio_driver mxl862xx_driver = { + .probe = mxl862xx_probe, + .remove = mxl862xx_remove, + .shutdown = mxl862xx_shutdown, + .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"); diff --git a/drivers/net/dsa/mxl862xx/mxl862xx.h b/drivers/net/dsa/mxl862xx/mxl862xx.h new file mode 100644 index 000000000000..bfeb436942d5 --- /dev/null +++ b/drivers/net/dsa/mxl862xx/mxl862xx.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#ifndef __MXL862XX_H +#define __MXL862XX_H + +#include +#include + +#define MXL862XX_MAX_PORTS 17 + +struct mxl862xx_priv { + struct dsa_switch *ds; + struct mdio_device *mdiodev; +}; + +#endif /* __MXL862XX_H */