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-mxl862xx-vlan-support-and-minor-improvements'

Daniel Golle says:

====================
net: dsa: mxl862xx: VLAN support and minor improvements

This series adds VLAN offloading to the mxl862xx DSA driver along
with two minor improvements to port setup and bridge configuration.
VLAN support uses a hybrid architecture combining the Extended VLAN
engine for PVID insertion and tag stripping with the VLAN Filter
engine for per-port VID membership, both drawing from shared
1024-entry hardware pools partitioned across user ports at probe time.
====================

Link: https://patch.msgid.link/cover.1775581804.git.daniel@makrotopia.org
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

+1219 -18
+329
drivers/net/dsa/mxl862xx/mxl862xx-api.h
··· 732 732 } __packed; 733 733 734 734 /** 735 + * enum mxl862xx_extended_vlan_filter_type - Extended VLAN filter tag type 736 + * @MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL: Normal tagged 737 + * @MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER: No filter (wildcard) 738 + * @MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT: Default entry 739 + * @MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG: Untagged 740 + */ 741 + enum mxl862xx_extended_vlan_filter_type { 742 + MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL = 0, 743 + MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER = 1, 744 + MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT = 2, 745 + MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG = 3, 746 + }; 747 + 748 + /** 749 + * enum mxl862xx_extended_vlan_filter_tpid - Extended VLAN filter TPID 750 + * @MXL862XX_EXTENDEDVLAN_FILTER_TPID_NO_FILTER: No TPID filter 751 + * @MXL862XX_EXTENDEDVLAN_FILTER_TPID_8021Q: 802.1Q TPID 752 + * @MXL862XX_EXTENDEDVLAN_FILTER_TPID_VTETYPE: VLAN type extension 753 + */ 754 + enum mxl862xx_extended_vlan_filter_tpid { 755 + MXL862XX_EXTENDEDVLAN_FILTER_TPID_NO_FILTER = 0, 756 + MXL862XX_EXTENDEDVLAN_FILTER_TPID_8021Q = 1, 757 + MXL862XX_EXTENDEDVLAN_FILTER_TPID_VTETYPE = 2, 758 + }; 759 + 760 + /** 761 + * enum mxl862xx_extended_vlan_filter_dei - Extended VLAN filter DEI 762 + * @MXL862XX_EXTENDEDVLAN_FILTER_DEI_NO_FILTER: No DEI filter 763 + * @MXL862XX_EXTENDEDVLAN_FILTER_DEI_0: DEI = 0 764 + * @MXL862XX_EXTENDEDVLAN_FILTER_DEI_1: DEI = 1 765 + */ 766 + enum mxl862xx_extended_vlan_filter_dei { 767 + MXL862XX_EXTENDEDVLAN_FILTER_DEI_NO_FILTER = 0, 768 + MXL862XX_EXTENDEDVLAN_FILTER_DEI_0 = 1, 769 + MXL862XX_EXTENDEDVLAN_FILTER_DEI_1 = 2, 770 + }; 771 + 772 + /** 773 + * enum mxl862xx_extended_vlan_treatment_remove_tag - Tag removal action 774 + * @MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG: Do not remove tag 775 + * @MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG: Remove one tag 776 + * @MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_2_TAG: Remove two tags 777 + * @MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM: Discard frame 778 + */ 779 + enum mxl862xx_extended_vlan_treatment_remove_tag { 780 + MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG = 0, 781 + MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG = 1, 782 + MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_2_TAG = 2, 783 + MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM = 3, 784 + }; 785 + 786 + /** 787 + * enum mxl862xx_extended_vlan_treatment_priority - Treatment priority mode 788 + * @MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL: Use explicit value 789 + * @MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_PRIORITY: Copy from inner tag 790 + * @MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_PRIORITY: Copy from outer tag 791 + * @MXL862XX_EXTENDEDVLAN_TREATMENT_DSCP: Derive from DSCP 792 + */ 793 + enum mxl862xx_extended_vlan_treatment_priority { 794 + MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL = 0, 795 + MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_PRIORITY = 1, 796 + MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_PRIORITY = 2, 797 + MXL862XX_EXTENDEDVLAN_TREATMENT_DSCP = 3, 798 + }; 799 + 800 + /** 801 + * enum mxl862xx_extended_vlan_treatment_vid - Treatment VID mode 802 + * @MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL: Use explicit VID value 803 + * @MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_VID: Copy from inner tag 804 + * @MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_VID: Copy from outer tag 805 + */ 806 + enum mxl862xx_extended_vlan_treatment_vid { 807 + MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL = 0, 808 + MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_VID = 1, 809 + MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_VID = 2, 810 + }; 811 + 812 + /** 813 + * enum mxl862xx_extended_vlan_treatment_tpid - Treatment TPID mode 814 + * @MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_TPID: Copy from inner tag 815 + * @MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_TPID: Copy from outer tag 816 + * @MXL862XX_EXTENDEDVLAN_TREATMENT_VTETYPE: Use VLAN type extension 817 + * @MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q: Use 802.1Q TPID 818 + */ 819 + enum mxl862xx_extended_vlan_treatment_tpid { 820 + MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_TPID = 0, 821 + MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_TPID = 1, 822 + MXL862XX_EXTENDEDVLAN_TREATMENT_VTETYPE = 2, 823 + MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q = 3, 824 + }; 825 + 826 + /** 827 + * enum mxl862xx_extended_vlan_treatment_dei - Treatment DEI mode 828 + * @MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_DEI: Copy from inner tag 829 + * @MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_DEI: Copy from outer tag 830 + * @MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0: Set DEI to 0 831 + * @MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_1: Set DEI to 1 832 + */ 833 + enum mxl862xx_extended_vlan_treatment_dei { 834 + MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_DEI = 0, 835 + MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_DEI = 1, 836 + MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0 = 2, 837 + MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_1 = 3, 838 + }; 839 + 840 + /** 841 + * enum mxl862xx_extended_vlan_4_tpid_mode - 4-TPID mode selector 842 + * @MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_1: VLAN TPID type 1 843 + * @MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_2: VLAN TPID type 2 844 + * @MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_3: VLAN TPID type 3 845 + * @MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_4: VLAN TPID type 4 846 + */ 847 + enum mxl862xx_extended_vlan_4_tpid_mode { 848 + MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_1 = 0, 849 + MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_2 = 1, 850 + MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_3 = 2, 851 + MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_4 = 3, 852 + }; 853 + 854 + /** 855 + * enum mxl862xx_extended_vlan_filter_ethertype - Filter EtherType match 856 + * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_NO_FILTER: No filter 857 + * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_IPOE: IPoE 858 + * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_PPPOE: PPPoE 859 + * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_ARP: ARP 860 + * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_IPV6IPOE: IPv6 IPoE 861 + * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_EAPOL: EAPOL 862 + * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_DHCPV4: DHCPv4 863 + * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_DHCPV6: DHCPv6 864 + */ 865 + enum mxl862xx_extended_vlan_filter_ethertype { 866 + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_NO_FILTER = 0, 867 + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_IPOE = 1, 868 + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_PPPOE = 2, 869 + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_ARP = 3, 870 + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_IPV6IPOE = 4, 871 + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_EAPOL = 5, 872 + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_DHCPV4 = 6, 873 + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_DHCPV6 = 7, 874 + }; 875 + 876 + /** 877 + * struct mxl862xx_extendedvlan_filter_vlan - Per-tag filter in Extended VLAN 878 + * @type: Tag presence/type match (see &enum mxl862xx_extended_vlan_filter_type) 879 + * @priority_enable: Enable PCP value matching 880 + * @priority_val: PCP value to match 881 + * @vid_enable: Enable VID matching 882 + * @vid_val: VID value to match 883 + * @tpid: TPID match mode (see &enum mxl862xx_extended_vlan_filter_tpid) 884 + * @dei: DEI match mode (see &enum mxl862xx_extended_vlan_filter_dei) 885 + */ 886 + struct mxl862xx_extendedvlan_filter_vlan { 887 + __le32 type; 888 + u8 priority_enable; 889 + __le32 priority_val; 890 + u8 vid_enable; 891 + __le32 vid_val; 892 + __le32 tpid; 893 + __le32 dei; 894 + } __packed; 895 + 896 + /** 897 + * struct mxl862xx_extendedvlan_filter - Extended VLAN filter configuration 898 + * @original_packet_filter_mode: If true, filter on original (pre-treatment) 899 + * packet 900 + * @filter_4_tpid_mode: 4-TPID mode (see &enum mxl862xx_extended_vlan_4_tpid_mode) 901 + * @outer_vlan: Outer VLAN tag filter 902 + * @inner_vlan: Inner VLAN tag filter 903 + * @ether_type: EtherType filter (see 904 + * &enum mxl862xx_extended_vlan_filter_ethertype) 905 + */ 906 + struct mxl862xx_extendedvlan_filter { 907 + u8 original_packet_filter_mode; 908 + __le32 filter_4_tpid_mode; 909 + struct mxl862xx_extendedvlan_filter_vlan outer_vlan; 910 + struct mxl862xx_extendedvlan_filter_vlan inner_vlan; 911 + __le32 ether_type; 912 + } __packed; 913 + 914 + /** 915 + * struct mxl862xx_extendedvlan_treatment_vlan - Per-tag treatment in 916 + * Extended VLAN 917 + * @priority_mode: Priority assignment mode 918 + * (see &enum mxl862xx_extended_vlan_treatment_priority) 919 + * @priority_val: Priority value (when mode is VAL) 920 + * @vid_mode: VID assignment mode 921 + * (see &enum mxl862xx_extended_vlan_treatment_vid) 922 + * @vid_val: VID value (when mode is VAL) 923 + * @tpid: TPID assignment mode 924 + * (see &enum mxl862xx_extended_vlan_treatment_tpid) 925 + * @dei: DEI assignment mode 926 + * (see &enum mxl862xx_extended_vlan_treatment_dei) 927 + */ 928 + struct mxl862xx_extendedvlan_treatment_vlan { 929 + __le32 priority_mode; 930 + __le32 priority_val; 931 + __le32 vid_mode; 932 + __le32 vid_val; 933 + __le32 tpid; 934 + __le32 dei; 935 + } __packed; 936 + 937 + /** 938 + * struct mxl862xx_extendedvlan_treatment - Extended VLAN treatment 939 + * @remove_tag: Tag removal action 940 + * (see &enum mxl862xx_extended_vlan_treatment_remove_tag) 941 + * @treatment_4_tpid_mode: 4-TPID treatment mode 942 + * @add_outer_vlan: Add outer VLAN tag 943 + * @outer_vlan: Outer VLAN tag treatment parameters 944 + * @add_inner_vlan: Add inner VLAN tag 945 + * @inner_vlan: Inner VLAN tag treatment parameters 946 + * @reassign_bridge_port: Reassign to different bridge port 947 + * @new_bridge_port_id: New bridge port ID 948 + * @new_dscp_enable: Enable new DSCP assignment 949 + * @new_dscp: New DSCP value 950 + * @new_traffic_class_enable: Enable new traffic class assignment 951 + * @new_traffic_class: New traffic class value 952 + * @new_meter_enable: Enable new metering 953 + * @s_new_traffic_meter_id: New traffic meter ID 954 + * @dscp2pcp_map: DSCP to PCP mapping table (64 entries) 955 + * @loopback_enable: Enable loopback 956 + * @da_sa_swap_enable: Enable DA/SA swap 957 + * @mirror_enable: Enable mirroring 958 + */ 959 + struct mxl862xx_extendedvlan_treatment { 960 + __le32 remove_tag; 961 + __le32 treatment_4_tpid_mode; 962 + u8 add_outer_vlan; 963 + struct mxl862xx_extendedvlan_treatment_vlan outer_vlan; 964 + u8 add_inner_vlan; 965 + struct mxl862xx_extendedvlan_treatment_vlan inner_vlan; 966 + u8 reassign_bridge_port; 967 + __le16 new_bridge_port_id; 968 + u8 new_dscp_enable; 969 + __le16 new_dscp; 970 + u8 new_traffic_class_enable; 971 + u8 new_traffic_class; 972 + u8 new_meter_enable; 973 + __le16 s_new_traffic_meter_id; 974 + u8 dscp2pcp_map[64]; 975 + u8 loopback_enable; 976 + u8 da_sa_swap_enable; 977 + u8 mirror_enable; 978 + } __packed; 979 + 980 + /** 981 + * struct mxl862xx_extendedvlan_alloc - Extended VLAN block allocation 982 + * @number_of_entries: Number of entries to allocate (input) / allocated 983 + * (output) 984 + * @extended_vlan_block_id: Block ID assigned by firmware (output on alloc, 985 + * input on free) 986 + * 987 + * Used with %MXL862XX_EXTENDEDVLAN_ALLOC and %MXL862XX_EXTENDEDVLAN_FREE. 988 + */ 989 + struct mxl862xx_extendedvlan_alloc { 990 + __le16 number_of_entries; 991 + __le16 extended_vlan_block_id; 992 + } __packed; 993 + 994 + /** 995 + * struct mxl862xx_extendedvlan_config - Extended VLAN entry configuration 996 + * @extended_vlan_block_id: Block ID from allocation 997 + * @entry_index: Entry index within the block 998 + * @filter: Filter (match) configuration 999 + * @treatment: Treatment (action) configuration 1000 + * 1001 + * Used with %MXL862XX_EXTENDEDVLAN_SET and %MXL862XX_EXTENDEDVLAN_GET. 1002 + */ 1003 + struct mxl862xx_extendedvlan_config { 1004 + __le16 extended_vlan_block_id; 1005 + __le16 entry_index; 1006 + struct mxl862xx_extendedvlan_filter filter; 1007 + struct mxl862xx_extendedvlan_treatment treatment; 1008 + } __packed; 1009 + 1010 + /** 1011 + * enum mxl862xx_vlan_filter_tci_mask - VLAN Filter TCI mask 1012 + * @MXL862XX_VLAN_FILTER_TCI_MASK_VID: TCI mask for VLAN ID 1013 + * @MXL862XX_VLAN_FILTER_TCI_MASK_PCP: TCI mask for VLAN PCP 1014 + * @MXL862XX_VLAN_FILTER_TCI_MASK_TCI: TCI mask for VLAN TCI 1015 + */ 1016 + enum mxl862xx_vlan_filter_tci_mask { 1017 + MXL862XX_VLAN_FILTER_TCI_MASK_VID = 0, 1018 + MXL862XX_VLAN_FILTER_TCI_MASK_PCP = 1, 1019 + MXL862XX_VLAN_FILTER_TCI_MASK_TCI = 2, 1020 + }; 1021 + 1022 + /** 1023 + * struct mxl862xx_vlanfilter_alloc - VLAN Filter block allocation 1024 + * @number_of_entries: Number of entries to allocate (input) / allocated 1025 + * (output) 1026 + * @vlan_filter_block_id: Block ID assigned by firmware (output on alloc, 1027 + * input on free) 1028 + * @discard_untagged: Discard untagged packets 1029 + * @discard_unmatched_tagged: Discard tagged packets that do not match any 1030 + * entry in the block 1031 + * @use_default_port_vid: Use default port VLAN ID for filtering 1032 + * 1033 + * Used with %MXL862XX_VLANFILTER_ALLOC and %MXL862XX_VLANFILTER_FREE. 1034 + */ 1035 + struct mxl862xx_vlanfilter_alloc { 1036 + __le16 number_of_entries; 1037 + __le16 vlan_filter_block_id; 1038 + u8 discard_untagged; 1039 + u8 discard_unmatched_tagged; 1040 + u8 use_default_port_vid; 1041 + } __packed; 1042 + 1043 + /** 1044 + * struct mxl862xx_vlanfilter_config - VLAN Filter entry configuration 1045 + * @vlan_filter_block_id: Block ID from allocation 1046 + * @entry_index: Entry index within the block 1047 + * @vlan_filter_mask: TCI field(s) to match (see 1048 + * &enum mxl862xx_vlan_filter_tci_mask) 1049 + * @val: TCI value(s) to match (VID, PCP, or full TCI depending on mask) 1050 + * @discard_matched: When true, discard frames matching this entry; 1051 + * when false, allow them 1052 + * 1053 + * Used with %MXL862XX_VLANFILTER_SET and %MXL862XX_VLANFILTER_GET. 1054 + */ 1055 + struct mxl862xx_vlanfilter_config { 1056 + __le16 vlan_filter_block_id; 1057 + __le16 entry_index; 1058 + __le32 vlan_filter_mask; /* enum mxl862xx_vlan_filter_tci_mask */ 1059 + __le32 val; 1060 + u8 discard_matched; 1061 + } __packed; 1062 + 1063 + /** 735 1064 * enum mxl862xx_ss_sp_tag_mask - Special tag valid field indicator bits 736 1065 * @MXL862XX_SS_SP_TAG_MASK_RX: valid RX special tag mode 737 1066 * @MXL862XX_SS_SP_TAG_MASK_TX: valid TX special tag mode
+12
drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
··· 17 17 #define MXL862XX_CTP_MAGIC 0x500 18 18 #define MXL862XX_QOS_MAGIC 0x600 19 19 #define MXL862XX_SWMAC_MAGIC 0xa00 20 + #define MXL862XX_EXTVLAN_MAGIC 0xb00 21 + #define MXL862XX_VLANFILTER_MAGIC 0xc00 20 22 #define MXL862XX_STP_MAGIC 0xf00 21 23 #define MXL862XX_SS_MAGIC 0x1600 22 24 #define GPY_GPY2XX_MAGIC 0x1800 ··· 48 46 #define MXL862XX_MAC_TABLEENTRYQUERY (MXL862XX_SWMAC_MAGIC + 0x4) 49 47 #define MXL862XX_MAC_TABLEENTRYREMOVE (MXL862XX_SWMAC_MAGIC + 0x5) 50 48 #define MXL862XX_MAC_TABLECLEARCOND (MXL862XX_SWMAC_MAGIC + 0x8) 49 + 50 + #define MXL862XX_EXTENDEDVLAN_ALLOC (MXL862XX_EXTVLAN_MAGIC + 0x1) 51 + #define MXL862XX_EXTENDEDVLAN_SET (MXL862XX_EXTVLAN_MAGIC + 0x2) 52 + #define MXL862XX_EXTENDEDVLAN_GET (MXL862XX_EXTVLAN_MAGIC + 0x3) 53 + #define MXL862XX_EXTENDEDVLAN_FREE (MXL862XX_EXTVLAN_MAGIC + 0x4) 54 + 55 + #define MXL862XX_VLANFILTER_ALLOC (MXL862XX_VLANFILTER_MAGIC + 0x1) 56 + #define MXL862XX_VLANFILTER_SET (MXL862XX_VLANFILTER_MAGIC + 0x2) 57 + #define MXL862XX_VLANFILTER_GET (MXL862XX_VLANFILTER_MAGIC + 0x3) 58 + #define MXL862XX_VLANFILTER_FREE (MXL862XX_VLANFILTER_MAGIC + 0x4) 51 59 52 60 #define MXL862XX_SS_SPTAG_SET (MXL862XX_SS_MAGIC + 0x2) 53 61
+786 -7
drivers/net/dsa/mxl862xx/mxl862xx.c
··· 50 50 MXL862XX_BRIDGE_PORT_EGRESS_METER_BROADCAST, 51 51 }; 52 52 53 + enum mxl862xx_evlan_action { 54 + EVLAN_ACCEPT, /* pass-through, no tag removal */ 55 + EVLAN_STRIP_IF_UNTAGGED, /* remove 1 tag if entry's untagged flag set */ 56 + EVLAN_PVID_OR_DISCARD, /* insert PVID tag or discard if no PVID */ 57 + EVLAN_STRIP1_AND_PVID_OR_DISCARD,/* strip 1 tag + insert PVID, or discard */ 58 + }; 59 + 60 + struct mxl862xx_evlan_rule_desc { 61 + u8 outer_type; /* enum mxl862xx_extended_vlan_filter_type */ 62 + u8 inner_type; /* enum mxl862xx_extended_vlan_filter_type */ 63 + u8 outer_tpid; /* enum mxl862xx_extended_vlan_filter_tpid */ 64 + u8 inner_tpid; /* enum mxl862xx_extended_vlan_filter_tpid */ 65 + bool match_vid; /* true: match on VID from the vid parameter */ 66 + u8 action; /* enum mxl862xx_evlan_action */ 67 + }; 68 + 69 + /* Shorthand constants for readability */ 70 + #define FT_NORMAL MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL 71 + #define FT_NO_FILTER MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER 72 + #define FT_DEFAULT MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT 73 + #define FT_NO_TAG MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG 74 + #define TP_NONE MXL862XX_EXTENDEDVLAN_FILTER_TPID_NO_FILTER 75 + #define TP_8021Q MXL862XX_EXTENDEDVLAN_FILTER_TPID_8021Q 76 + 77 + /* 78 + * VLAN-aware ingress: 7 final catchall rules. 79 + * 80 + * VLAN Filter handles VID membership for tagged frames, so the 81 + * Extended VLAN ingress block only needs to handle: 82 + * - Priority-tagged (VID=0): strip + insert PVID 83 + * - Untagged: insert PVID or discard 84 + * - Standard 802.1Q VID>0: pass through (VF handles membership) 85 + * - Non-8021Q TPID (0x88A8 etc.): treat as untagged 86 + * 87 + * Rule ordering is critical: the EVLAN engine scans entries in 88 + * ascending index order and stops at the first match. 89 + * 90 + * The 802.1Q ACCEPT rules (indices 3--4) must appear BEFORE the 91 + * NO_FILTER catchalls (indices 5--6). NO_FILTER matches any tag 92 + * regardless of TPID, so without the ACCEPT guard, it would also 93 + * catch standard 802.1Q VID>0 frames and corrupt them. With the 94 + * guard, 802.1Q VID>0 frames match the ACCEPT rules first and 95 + * pass through untouched; only non-8021Q TPID frames pass through 96 + * to the NO_FILTER catchalls. 97 + */ 98 + static const struct mxl862xx_evlan_rule_desc ingress_aware_final[] = { 99 + /* 802.1p / priority-tagged (VID 0): strip + PVID */ 100 + { FT_NORMAL, FT_NORMAL, TP_8021Q, TP_8021Q, true, EVLAN_STRIP1_AND_PVID_OR_DISCARD }, 101 + { FT_NORMAL, FT_NO_TAG, TP_8021Q, TP_NONE, true, EVLAN_STRIP1_AND_PVID_OR_DISCARD }, 102 + /* Untagged: PVID insertion or discard */ 103 + { FT_NO_TAG, FT_NO_TAG, TP_NONE, TP_NONE, false, EVLAN_PVID_OR_DISCARD }, 104 + /* 802.1Q VID>0: accept - VF handles membership. 105 + * match_vid=false means any VID; VID=0 is already caught above. 106 + */ 107 + { FT_NORMAL, FT_NORMAL, TP_8021Q, TP_8021Q, false, EVLAN_ACCEPT }, 108 + { FT_NORMAL, FT_NO_TAG, TP_8021Q, TP_NONE, false, EVLAN_ACCEPT }, 109 + /* Non-8021Q TPID (0x88A8 etc.): treat as untagged - strip + PVID */ 110 + { FT_NO_FILTER, FT_NO_FILTER, TP_NONE, TP_NONE, false, EVLAN_STRIP1_AND_PVID_OR_DISCARD }, 111 + { FT_NO_FILTER, FT_NO_TAG, TP_NONE, TP_NONE, false, EVLAN_STRIP1_AND_PVID_OR_DISCARD }, 112 + }; 113 + 114 + /* 115 + * VID-specific accept rules (VLAN-aware, standard tag, 2 per VID). 116 + * Outer tag carries the VLAN; inner may or may not be present. 117 + */ 118 + static const struct mxl862xx_evlan_rule_desc vid_accept_standard[] = { 119 + { FT_NORMAL, FT_NORMAL, TP_8021Q, TP_8021Q, true, EVLAN_STRIP_IF_UNTAGGED }, 120 + { FT_NORMAL, FT_NO_TAG, TP_8021Q, TP_NONE, true, EVLAN_STRIP_IF_UNTAGGED }, 121 + }; 122 + 123 + /* 124 + * Egress tag-stripping rules for VLAN-unaware mode (2 per untagged VID). 125 + * The HW sees the MxL tag as outer; the real VLAN tag, if any, is inner. 126 + */ 127 + static const struct mxl862xx_evlan_rule_desc vid_accept_egress_unaware[] = { 128 + { FT_NO_FILTER, FT_NORMAL, TP_NONE, TP_8021Q, true, EVLAN_STRIP_IF_UNTAGGED }, 129 + { FT_NO_FILTER, FT_NO_TAG, TP_NONE, TP_NONE, false, EVLAN_STRIP_IF_UNTAGGED }, 130 + }; 131 + 53 132 static enum dsa_tag_protocol mxl862xx_get_tag_protocol(struct dsa_switch *ds, 54 133 int port, 55 134 enum dsa_tag_protocol m) ··· 354 275 struct mxl862xx_port *p = &priv->ports[port]; 355 276 struct dsa_port *member_dp; 356 277 u16 bridge_id; 278 + u16 vf_scan; 357 279 bool enable; 358 280 int i, idx; 359 281 360 - if (!p->setup_done) 282 + if (dsa_port_is_unused(dp)) 361 283 return 0; 362 284 363 285 if (dsa_port_is_cpu(dp)) { ··· 392 312 br_port_cfg.mask = cpu_to_le32(MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID | 393 313 MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP | 394 314 MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING | 395 - MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_SUB_METER); 315 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_SUB_METER | 316 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN | 317 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN | 318 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN_FILTER | 319 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN_FILTER1 | 320 + MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MAC_LEARNING); 396 321 br_port_cfg.src_mac_learning_disable = !p->learning; 322 + 323 + /* Extended VLAN block assignments. 324 + * Ingress: block_size is sent as-is (all entries are finals). 325 + * Egress: n_active narrows the scan window to only the 326 + * entries actually written by evlan_program_egress. 327 + */ 328 + br_port_cfg.ingress_extended_vlan_enable = p->ingress_evlan.in_use; 329 + br_port_cfg.ingress_extended_vlan_block_id = 330 + cpu_to_le16(p->ingress_evlan.block_id); 331 + br_port_cfg.ingress_extended_vlan_block_size = 332 + cpu_to_le16(p->ingress_evlan.block_size); 333 + br_port_cfg.egress_extended_vlan_enable = p->egress_evlan.in_use; 334 + br_port_cfg.egress_extended_vlan_block_id = 335 + cpu_to_le16(p->egress_evlan.block_id); 336 + br_port_cfg.egress_extended_vlan_block_size = 337 + cpu_to_le16(p->egress_evlan.n_active); 338 + 339 + /* VLAN Filter block assignments (per-port). 340 + * The block_size sent to the firmware narrows the HW scan 341 + * window to [block_id, block_id + active_count), relying on 342 + * discard_unmatched_tagged for frames outside that range. 343 + * When active_count=0, send 1 to scan only the DISCARD 344 + * sentinel at index 0 (block_size=0 would disable narrowing 345 + * and scan the entire allocated block). 346 + * 347 + * The bridge check ensures VF is disabled when the port 348 + * leaves the bridge, without needing to prematurely clear 349 + * vlan_filtering (which the DSA framework handles later via 350 + * port_vlan_filtering). 351 + */ 352 + if (p->vf.allocated && p->vlan_filtering && 353 + dsa_port_bridge_dev_get(dp)) { 354 + vf_scan = max_t(u16, p->vf.active_count, 1); 355 + br_port_cfg.ingress_vlan_filter_enable = 1; 356 + br_port_cfg.ingress_vlan_filter_block_id = 357 + cpu_to_le16(p->vf.block_id); 358 + br_port_cfg.ingress_vlan_filter_block_size = 359 + cpu_to_le16(vf_scan); 360 + 361 + br_port_cfg.egress_vlan_filter1enable = 1; 362 + br_port_cfg.egress_vlan_filter1block_id = 363 + cpu_to_le16(p->vf.block_id); 364 + br_port_cfg.egress_vlan_filter1block_size = 365 + cpu_to_le16(vf_scan); 366 + } else { 367 + br_port_cfg.ingress_vlan_filter_enable = 0; 368 + br_port_cfg.egress_vlan_filter1enable = 0; 369 + } 370 + 371 + /* IVL when VLAN-aware: include VID in FDB lookup keys so that 372 + * learned entries are per-VID. In VLAN-unaware mode, SVL is 373 + * used (VID excluded from key). 374 + */ 375 + br_port_cfg.vlan_src_mac_vid_enable = p->vlan_filtering; 376 + br_port_cfg.vlan_dst_mac_vid_enable = p->vlan_filtering; 397 377 398 378 for (i = 0; i < ARRAY_SIZE(mxl862xx_flood_meters); i++) { 399 379 idx = mxl862xx_flood_meters[i]; ··· 481 341 } 482 342 483 343 return ret; 344 + } 345 + 346 + static int mxl862xx_evlan_block_alloc(struct mxl862xx_priv *priv, 347 + struct mxl862xx_evlan_block *blk) 348 + { 349 + struct mxl862xx_extendedvlan_alloc param = {}; 350 + int ret; 351 + 352 + param.number_of_entries = cpu_to_le16(blk->block_size); 353 + 354 + ret = MXL862XX_API_READ(priv, MXL862XX_EXTENDEDVLAN_ALLOC, param); 355 + if (ret) 356 + return ret; 357 + 358 + blk->block_id = le16_to_cpu(param.extended_vlan_block_id); 359 + blk->allocated = true; 360 + 361 + return 0; 362 + } 363 + 364 + static int mxl862xx_vf_block_alloc(struct mxl862xx_priv *priv, 365 + u16 size, u16 *block_id) 366 + { 367 + struct mxl862xx_vlanfilter_alloc param = {}; 368 + int ret; 369 + 370 + param.number_of_entries = cpu_to_le16(size); 371 + param.discard_untagged = 0; 372 + param.discard_unmatched_tagged = 1; 373 + 374 + ret = MXL862XX_API_READ(priv, MXL862XX_VLANFILTER_ALLOC, param); 375 + if (ret) 376 + return ret; 377 + 378 + *block_id = le16_to_cpu(param.vlan_filter_block_id); 379 + return 0; 380 + } 381 + 382 + static int mxl862xx_vf_entry_discard(struct mxl862xx_priv *priv, 383 + u16 block_id, u16 index) 384 + { 385 + struct mxl862xx_vlanfilter_config cfg = {}; 386 + 387 + cfg.vlan_filter_block_id = cpu_to_le16(block_id); 388 + cfg.entry_index = cpu_to_le16(index); 389 + cfg.vlan_filter_mask = cpu_to_le32(MXL862XX_VLAN_FILTER_TCI_MASK_VID); 390 + cfg.val = cpu_to_le32(0); 391 + cfg.discard_matched = 1; 392 + 393 + return MXL862XX_API_WRITE(priv, MXL862XX_VLANFILTER_SET, cfg); 394 + } 395 + 396 + static int mxl862xx_vf_alloc(struct mxl862xx_priv *priv, 397 + struct mxl862xx_vf_block *vf) 398 + { 399 + int ret; 400 + 401 + ret = mxl862xx_vf_block_alloc(priv, vf->block_size, &vf->block_id); 402 + if (ret) 403 + return ret; 404 + 405 + vf->allocated = true; 406 + vf->active_count = 0; 407 + 408 + /* Sentinel: block VID-0 when scan window covers only index 0 */ 409 + return mxl862xx_vf_entry_discard(priv, vf->block_id, 0); 484 410 } 485 411 486 412 static int mxl862xx_allocate_bridge(struct mxl862xx_priv *priv) ··· 584 378 static int mxl862xx_setup(struct dsa_switch *ds) 585 379 { 586 380 struct mxl862xx_priv *priv = ds->priv; 381 + int n_user_ports = 0, max_vlans; 382 + int ingress_finals, vid_rules; 383 + struct dsa_port *dp; 587 384 int ret; 588 385 589 386 ret = mxl862xx_reset(priv); ··· 596 387 ret = mxl862xx_wait_ready(ds); 597 388 if (ret) 598 389 return ret; 390 + 391 + /* Calculate Extended VLAN block sizes. 392 + * With VLAN Filter handling VID membership checks: 393 + * Ingress: only final catchall rules (PVID insertion, 802.1Q 394 + * accept, non-8021Q TPID handling, discard). 395 + * Block sized to exactly fit the finals -- no per-VID 396 + * ingress EVLAN rules are needed. (7 entries.) 397 + * Egress: 2 rules per VID that needs tag stripping (untagged VIDs). 398 + * No egress final catchalls -- VLAN Filter does the discard. 399 + * CPU: EVLAN is left disabled on CPU ports -- frames pass 400 + * through without EVLAN processing. 401 + * 402 + * Total EVLAN budget: 403 + * n_user_ports * (ingress + egress) <= 1024. 404 + * Ingress blocks are small (7 entries), so almost all capacity 405 + * goes to egress VID rules. 406 + */ 407 + dsa_switch_for_each_user_port(dp, ds) 408 + n_user_ports++; 409 + 410 + if (n_user_ports) { 411 + ingress_finals = ARRAY_SIZE(ingress_aware_final); 412 + vid_rules = ARRAY_SIZE(vid_accept_standard); 413 + 414 + /* Ingress block: fixed at finals count (7 entries) */ 415 + priv->evlan_ingress_size = ingress_finals; 416 + 417 + /* Egress block: remaining budget divided equally among 418 + * user ports. Each untagged VID needs vid_rules (2) 419 + * EVLAN entries for tag stripping. Tagged-only VIDs 420 + * need no EVLAN rules at all. 421 + */ 422 + max_vlans = (MXL862XX_TOTAL_EVLAN_ENTRIES - 423 + n_user_ports * ingress_finals) / 424 + (n_user_ports * vid_rules); 425 + priv->evlan_egress_size = vid_rules * max_vlans; 426 + 427 + /* VLAN Filter block: one per user port. The 1024-entry 428 + * table is divided equally among user ports. Each port 429 + * gets its own VF block for per-port VID membership -- 430 + * discard_unmatched_tagged handles the rest. 431 + */ 432 + priv->vf_block_size = MXL862XX_TOTAL_VF_ENTRIES / n_user_ports; 433 + } 599 434 600 435 ret = mxl862xx_setup_drop_meter(ds); 601 436 if (ret) ··· 722 469 return MXL862XX_API_WRITE(ds->priv, MXL862XX_SS_SPTAG_SET, tag); 723 470 } 724 471 472 + static int mxl862xx_evlan_write_rule(struct mxl862xx_priv *priv, 473 + u16 block_id, u16 entry_index, 474 + const struct mxl862xx_evlan_rule_desc *desc, 475 + u16 vid, bool untagged, u16 pvid) 476 + { 477 + struct mxl862xx_extendedvlan_config cfg = {}; 478 + struct mxl862xx_extendedvlan_filter_vlan *fv; 479 + 480 + cfg.extended_vlan_block_id = cpu_to_le16(block_id); 481 + cfg.entry_index = cpu_to_le16(entry_index); 482 + 483 + /* Populate filter */ 484 + cfg.filter.outer_vlan.type = cpu_to_le32(desc->outer_type); 485 + cfg.filter.inner_vlan.type = cpu_to_le32(desc->inner_type); 486 + cfg.filter.outer_vlan.tpid = cpu_to_le32(desc->outer_tpid); 487 + cfg.filter.inner_vlan.tpid = cpu_to_le32(desc->inner_tpid); 488 + 489 + if (desc->match_vid) { 490 + /* For egress unaware: outer=NO_FILTER, match on inner tag */ 491 + if (desc->outer_type == FT_NO_FILTER) 492 + fv = &cfg.filter.inner_vlan; 493 + else 494 + fv = &cfg.filter.outer_vlan; 495 + 496 + fv->vid_enable = 1; 497 + fv->vid_val = cpu_to_le32(vid); 498 + } 499 + 500 + /* Populate treatment based on action */ 501 + switch (desc->action) { 502 + case EVLAN_ACCEPT: 503 + cfg.treatment.remove_tag = 504 + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG); 505 + break; 506 + 507 + case EVLAN_STRIP_IF_UNTAGGED: 508 + cfg.treatment.remove_tag = cpu_to_le32(untagged ? 509 + MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG : 510 + MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG); 511 + break; 512 + 513 + case EVLAN_PVID_OR_DISCARD: 514 + if (pvid) { 515 + cfg.treatment.remove_tag = 516 + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG); 517 + cfg.treatment.add_outer_vlan = 1; 518 + cfg.treatment.outer_vlan.vid_mode = 519 + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL); 520 + cfg.treatment.outer_vlan.vid_val = cpu_to_le32(pvid); 521 + cfg.treatment.outer_vlan.tpid = 522 + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q); 523 + } else { 524 + cfg.treatment.remove_tag = 525 + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM); 526 + } 527 + break; 528 + 529 + case EVLAN_STRIP1_AND_PVID_OR_DISCARD: 530 + if (pvid) { 531 + cfg.treatment.remove_tag = 532 + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG); 533 + cfg.treatment.add_outer_vlan = 1; 534 + cfg.treatment.outer_vlan.vid_mode = 535 + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL); 536 + cfg.treatment.outer_vlan.vid_val = cpu_to_le32(pvid); 537 + cfg.treatment.outer_vlan.tpid = 538 + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q); 539 + } else { 540 + cfg.treatment.remove_tag = 541 + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM); 542 + } 543 + break; 544 + } 545 + 546 + return MXL862XX_API_WRITE(priv, MXL862XX_EXTENDEDVLAN_SET, cfg); 547 + } 548 + 549 + static int mxl862xx_evlan_deactivate_entry(struct mxl862xx_priv *priv, 550 + u16 block_id, u16 entry_index) 551 + { 552 + struct mxl862xx_extendedvlan_config cfg = {}; 553 + 554 + cfg.extended_vlan_block_id = cpu_to_le16(block_id); 555 + cfg.entry_index = cpu_to_le16(entry_index); 556 + 557 + /* Use an unreachable filter (DEFAULT+DEFAULT) with DISCARD treatment. 558 + * A zeroed entry would have NORMAL+NORMAL filter which matches 559 + * real double-tagged traffic and passes it through. 560 + */ 561 + cfg.filter.outer_vlan.type = 562 + cpu_to_le32(MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT); 563 + cfg.filter.inner_vlan.type = 564 + cpu_to_le32(MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT); 565 + cfg.treatment.remove_tag = 566 + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM); 567 + 568 + return MXL862XX_API_WRITE(priv, MXL862XX_EXTENDEDVLAN_SET, cfg); 569 + } 570 + 571 + static int mxl862xx_evlan_write_final_rules(struct mxl862xx_priv *priv, 572 + struct mxl862xx_evlan_block *blk, 573 + const struct mxl862xx_evlan_rule_desc *rules, 574 + int n_rules, u16 pvid) 575 + { 576 + u16 start_idx = blk->block_size - n_rules; 577 + int i, ret; 578 + 579 + for (i = 0; i < n_rules; i++) { 580 + ret = mxl862xx_evlan_write_rule(priv, blk->block_id, 581 + start_idx + i, &rules[i], 582 + 0, false, pvid); 583 + if (ret) 584 + return ret; 585 + } 586 + 587 + return 0; 588 + } 589 + 590 + static int mxl862xx_vf_entry_set(struct mxl862xx_priv *priv, 591 + u16 block_id, u16 index, u16 vid) 592 + { 593 + struct mxl862xx_vlanfilter_config cfg = {}; 594 + 595 + cfg.vlan_filter_block_id = cpu_to_le16(block_id); 596 + cfg.entry_index = cpu_to_le16(index); 597 + cfg.vlan_filter_mask = cpu_to_le32(MXL862XX_VLAN_FILTER_TCI_MASK_VID); 598 + cfg.val = cpu_to_le32(vid); 599 + cfg.discard_matched = 0; 600 + 601 + return MXL862XX_API_WRITE(priv, MXL862XX_VLANFILTER_SET, cfg); 602 + } 603 + 604 + static struct mxl862xx_vf_vid *mxl862xx_vf_find_vid(struct mxl862xx_vf_block *vf, 605 + u16 vid) 606 + { 607 + struct mxl862xx_vf_vid *ve; 608 + 609 + list_for_each_entry(ve, &vf->vids, list) 610 + if (ve->vid == vid) 611 + return ve; 612 + 613 + return NULL; 614 + } 615 + 616 + static int mxl862xx_vf_add_vid(struct mxl862xx_priv *priv, 617 + struct mxl862xx_vf_block *vf, 618 + u16 vid, bool untagged) 619 + { 620 + struct mxl862xx_vf_vid *ve; 621 + int ret; 622 + 623 + ve = mxl862xx_vf_find_vid(vf, vid); 624 + if (ve) { 625 + ve->untagged = untagged; 626 + return 0; 627 + } 628 + 629 + if (vf->active_count >= vf->block_size) 630 + return -ENOSPC; 631 + 632 + ve = kzalloc_obj(*ve); 633 + if (!ve) 634 + return -ENOMEM; 635 + 636 + ve->vid = vid; 637 + ve->index = vf->active_count; 638 + ve->untagged = untagged; 639 + 640 + ret = mxl862xx_vf_entry_set(priv, vf->block_id, ve->index, vid); 641 + if (ret) { 642 + kfree(ve); 643 + return ret; 644 + } 645 + 646 + list_add_tail(&ve->list, &vf->vids); 647 + vf->active_count++; 648 + 649 + return 0; 650 + } 651 + 652 + static int mxl862xx_vf_del_vid(struct mxl862xx_priv *priv, 653 + struct mxl862xx_vf_block *vf, u16 vid) 654 + { 655 + struct mxl862xx_vf_vid *ve, *last_ve; 656 + u16 gap, last; 657 + int ret; 658 + 659 + ve = mxl862xx_vf_find_vid(vf, vid); 660 + if (!ve) 661 + return 0; 662 + 663 + if (!vf->allocated) { 664 + /* Software-only state -- just remove the tracking entry */ 665 + list_del(&ve->list); 666 + kfree(ve); 667 + vf->active_count--; 668 + return 0; 669 + } 670 + 671 + gap = ve->index; 672 + last = vf->active_count - 1; 673 + 674 + if (vf->active_count == 1) { 675 + /* Last VID -- restore DISCARD sentinel at index 0 */ 676 + ret = mxl862xx_vf_entry_discard(priv, vf->block_id, 0); 677 + if (ret) 678 + return ret; 679 + } else if (gap < last) { 680 + /* Swap: move the last ALLOW entry into the gap */ 681 + list_for_each_entry(last_ve, &vf->vids, list) 682 + if (last_ve->index == last) 683 + break; 684 + 685 + if (WARN_ON(list_entry_is_head(last_ve, &vf->vids, list))) 686 + return -EINVAL; 687 + 688 + ret = mxl862xx_vf_entry_set(priv, vf->block_id, 689 + gap, last_ve->vid); 690 + if (ret) 691 + return ret; 692 + 693 + last_ve->index = gap; 694 + } 695 + 696 + list_del(&ve->list); 697 + kfree(ve); 698 + vf->active_count--; 699 + 700 + return 0; 701 + } 702 + 703 + static int mxl862xx_evlan_program_ingress(struct mxl862xx_priv *priv, int port) 704 + { 705 + struct mxl862xx_port *p = &priv->ports[port]; 706 + struct mxl862xx_evlan_block *blk = &p->ingress_evlan; 707 + 708 + if (!p->vlan_filtering) 709 + return 0; 710 + 711 + blk->in_use = true; 712 + blk->n_active = blk->block_size; 713 + 714 + return mxl862xx_evlan_write_final_rules(priv, blk, 715 + ingress_aware_final, 716 + ARRAY_SIZE(ingress_aware_final), 717 + p->pvid); 718 + } 719 + 720 + static int mxl862xx_evlan_program_egress(struct mxl862xx_priv *priv, int port) 721 + { 722 + struct mxl862xx_port *p = &priv->ports[port]; 723 + struct mxl862xx_evlan_block *blk = &p->egress_evlan; 724 + const struct mxl862xx_evlan_rule_desc *vid_rules; 725 + struct mxl862xx_vf_vid *vfv; 726 + u16 old_active = blk->n_active; 727 + u16 idx = 0, i; 728 + int n_vid, ret; 729 + 730 + if (p->vlan_filtering) { 731 + vid_rules = vid_accept_standard; 732 + n_vid = ARRAY_SIZE(vid_accept_standard); 733 + } else { 734 + vid_rules = vid_accept_egress_unaware; 735 + n_vid = ARRAY_SIZE(vid_accept_egress_unaware); 736 + } 737 + 738 + list_for_each_entry(vfv, &p->vf.vids, list) { 739 + if (!vfv->untagged) 740 + continue; 741 + 742 + if (idx + n_vid > blk->block_size) 743 + return -ENOSPC; 744 + 745 + ret = mxl862xx_evlan_write_rule(priv, blk->block_id, 746 + idx++, &vid_rules[0], 747 + vfv->vid, vfv->untagged, 748 + p->pvid); 749 + if (ret) 750 + return ret; 751 + 752 + if (n_vid > 1) { 753 + ret = mxl862xx_evlan_write_rule(priv, blk->block_id, 754 + idx++, &vid_rules[1], 755 + vfv->vid, 756 + vfv->untagged, 757 + p->pvid); 758 + if (ret) 759 + return ret; 760 + } 761 + } 762 + 763 + /* Deactivate stale entries that are no longer needed. 764 + * This closes the brief window between writing the new rules 765 + * and set_bridge_port narrowing the scan window. 766 + */ 767 + for (i = idx; i < old_active; i++) { 768 + ret = mxl862xx_evlan_deactivate_entry(priv, 769 + blk->block_id, 770 + i); 771 + if (ret) 772 + return ret; 773 + } 774 + 775 + blk->n_active = idx; 776 + blk->in_use = idx > 0; 777 + 778 + return 0; 779 + } 780 + 781 + static int mxl862xx_port_vlan_filtering(struct dsa_switch *ds, int port, 782 + bool vlan_filtering, 783 + struct netlink_ext_ack *extack) 784 + { 785 + struct mxl862xx_priv *priv = ds->priv; 786 + struct mxl862xx_port *p = &priv->ports[port]; 787 + bool old_vlan_filtering = p->vlan_filtering; 788 + bool old_in_use = p->ingress_evlan.in_use; 789 + bool changed = (p->vlan_filtering != vlan_filtering); 790 + int ret; 791 + 792 + p->vlan_filtering = vlan_filtering; 793 + 794 + if (changed) { 795 + /* When leaving VLAN-aware mode, release the ingress HW 796 + * block. The firmware passes frames through unchanged 797 + * when no ingress EVLAN block is assigned, so the block 798 + * is unnecessary in unaware mode. 799 + */ 800 + if (!vlan_filtering) 801 + p->ingress_evlan.in_use = false; 802 + 803 + ret = mxl862xx_evlan_program_ingress(priv, port); 804 + if (ret) 805 + goto err_restore; 806 + 807 + ret = mxl862xx_evlan_program_egress(priv, port); 808 + if (ret) 809 + goto err_restore; 810 + } 811 + 812 + return mxl862xx_set_bridge_port(ds, port); 813 + 814 + /* No HW rollback -- restoring SW state is sufficient for a correct retry. */ 815 + err_restore: 816 + p->vlan_filtering = old_vlan_filtering; 817 + p->ingress_evlan.in_use = old_in_use; 818 + return ret; 819 + } 820 + 821 + static int mxl862xx_port_vlan_add(struct dsa_switch *ds, int port, 822 + const struct switchdev_obj_port_vlan *vlan, 823 + struct netlink_ext_ack *extack) 824 + { 825 + struct mxl862xx_priv *priv = ds->priv; 826 + struct mxl862xx_port *p = &priv->ports[port]; 827 + bool untagged = !!(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED); 828 + u16 vid = vlan->vid; 829 + u16 old_pvid = p->pvid; 830 + bool pvid_changed = false; 831 + int ret; 832 + 833 + /* CPU port is VLAN-transparent: the SP tag handles port 834 + * identification and the host-side DSA tagger manages VLAN 835 + * delivery. Egress EVLAN catchalls are set up once in 836 + * setup_cpu_bridge; no per-VID VF/EVLAN programming needed. 837 + */ 838 + if (dsa_is_cpu_port(ds, port)) 839 + return 0; 840 + 841 + /* Update PVID tracking */ 842 + if (vlan->flags & BRIDGE_VLAN_INFO_PVID) { 843 + if (p->pvid != vid) { 844 + p->pvid = vid; 845 + pvid_changed = true; 846 + } 847 + } else if (p->pvid == vid) { 848 + p->pvid = 0; 849 + pvid_changed = true; 850 + } 851 + 852 + /* Add/update VID in this port's VLAN Filter block. 853 + * VF must be updated before programming egress EVLAN because 854 + * evlan_program_egress walks the VF VID list. 855 + */ 856 + ret = mxl862xx_vf_add_vid(priv, &p->vf, vid, untagged); 857 + if (ret) 858 + goto err_pvid; 859 + 860 + /* Reprogram ingress finals if PVID changed */ 861 + if (pvid_changed) { 862 + ret = mxl862xx_evlan_program_ingress(priv, port); 863 + if (ret) 864 + goto err_rollback; 865 + } 866 + 867 + /* Reprogram egress tag-stripping rules (walks VF VID list) */ 868 + ret = mxl862xx_evlan_program_egress(priv, port); 869 + if (ret) 870 + goto err_rollback; 871 + 872 + /* Apply VLAN block IDs and MAC learning flags to bridge port */ 873 + ret = mxl862xx_set_bridge_port(ds, port); 874 + if (ret) 875 + goto err_rollback; 876 + 877 + return 0; 878 + 879 + err_rollback: 880 + /* Best-effort: undo VF add and restore consistent hardware state. 881 + * A retry of port_vlan_add will converge since vf_add_vid is 882 + * idempotent. 883 + */ 884 + p->pvid = old_pvid; 885 + mxl862xx_vf_del_vid(priv, &p->vf, vid); 886 + mxl862xx_evlan_program_ingress(priv, port); 887 + mxl862xx_evlan_program_egress(priv, port); 888 + mxl862xx_set_bridge_port(ds, port); 889 + return ret; 890 + err_pvid: 891 + p->pvid = old_pvid; 892 + return ret; 893 + } 894 + 895 + static int mxl862xx_port_vlan_del(struct dsa_switch *ds, int port, 896 + const struct switchdev_obj_port_vlan *vlan) 897 + { 898 + struct mxl862xx_priv *priv = ds->priv; 899 + struct mxl862xx_port *p = &priv->ports[port]; 900 + struct mxl862xx_vf_vid *ve; 901 + bool pvid_changed = false; 902 + u16 vid = vlan->vid; 903 + bool old_untagged; 904 + u16 old_pvid; 905 + int ret; 906 + 907 + if (dsa_is_cpu_port(ds, port)) 908 + return 0; 909 + 910 + ve = mxl862xx_vf_find_vid(&p->vf, vid); 911 + if (!ve) 912 + return 0; 913 + old_untagged = ve->untagged; 914 + old_pvid = p->pvid; 915 + 916 + /* Clear PVID if we're deleting it */ 917 + if (p->pvid == vid) { 918 + p->pvid = 0; 919 + pvid_changed = true; 920 + } 921 + 922 + /* Remove VID from this port's VLAN Filter block. 923 + * Must happen before egress reprogram so the VID is no 924 + * longer in the list that evlan_program_egress walks. 925 + */ 926 + ret = mxl862xx_vf_del_vid(priv, &p->vf, vid); 927 + if (ret) 928 + goto err_pvid; 929 + 930 + /* Reprogram egress tag-stripping rules (VID is now gone) */ 931 + ret = mxl862xx_evlan_program_egress(priv, port); 932 + if (ret) 933 + goto err_rollback; 934 + 935 + /* If PVID changed, reprogram ingress finals */ 936 + if (pvid_changed) { 937 + ret = mxl862xx_evlan_program_ingress(priv, port); 938 + if (ret) 939 + goto err_rollback; 940 + } 941 + 942 + ret = mxl862xx_set_bridge_port(ds, port); 943 + if (ret) 944 + goto err_rollback; 945 + 946 + return 0; 947 + 948 + err_rollback: 949 + /* Best-effort: re-add the VID and restore consistent hardware 950 + * state. A retry of port_vlan_del will converge. 951 + */ 952 + p->pvid = old_pvid; 953 + mxl862xx_vf_add_vid(priv, &p->vf, vid, old_untagged); 954 + mxl862xx_evlan_program_egress(priv, port); 955 + mxl862xx_evlan_program_ingress(priv, port); 956 + mxl862xx_set_bridge_port(ds, port); 957 + return ret; 958 + err_pvid: 959 + p->pvid = old_pvid; 960 + return ret; 961 + } 962 + 725 963 static int mxl862xx_setup_cpu_bridge(struct dsa_switch *ds, int port) 726 964 { 727 965 struct mxl862xx_priv *priv = ds->priv; 966 + struct mxl862xx_port *p = &priv->ports[port]; 728 967 729 - priv->ports[port].fid = MXL862XX_DEFAULT_BRIDGE; 730 - priv->ports[port].learning = true; 968 + p->fid = MXL862XX_DEFAULT_BRIDGE; 969 + p->learning = true; 970 + 971 + /* EVLAN is left disabled on CPU ports -- frames pass through 972 + * without EVLAN processing. Only the portmap and bridge 973 + * assignment need to be configured. 974 + */ 731 975 732 976 return mxl862xx_set_bridge_port(ds, port); 733 977 } ··· 1260 510 static void mxl862xx_port_bridge_leave(struct dsa_switch *ds, int port, 1261 511 const struct dsa_bridge bridge) 1262 512 { 513 + struct mxl862xx_priv *priv = ds->priv; 514 + struct mxl862xx_port *p = &priv->ports[port]; 1263 515 int err; 1264 516 1265 517 err = mxl862xx_sync_bridge_members(ds, &bridge); ··· 1273 521 /* Revert leaving port, omitted by the sync above, to its 1274 522 * single-port bridge 1275 523 */ 524 + p->pvid = 0; 525 + p->ingress_evlan.in_use = false; 526 + p->egress_evlan.in_use = false; 527 + 1276 528 err = mxl862xx_set_bridge_port(ds, port); 1277 529 if (err) 1278 530 dev_err(ds->dev, ··· 1300 544 1301 545 mxl862xx_port_fast_age(ds, port); 1302 546 1303 - if (dsa_port_is_unused(dp) || 1304 - dsa_port_is_dsa(dp)) 547 + if (dsa_port_is_unused(dp)) 1305 548 return 0; 549 + 550 + if (dsa_port_is_dsa(dp)) { 551 + dev_err(ds->dev, "port %d: DSA links not supported\n", port); 552 + return -EOPNOTSUPP; 553 + } 1306 554 1307 555 ret = mxl862xx_configure_sp_tag_proto(ds, port, is_cpu_port); 1308 556 if (ret) ··· 1341 581 if (ret) 1342 582 return ret; 1343 583 584 + priv->ports[port].ingress_evlan.block_size = priv->evlan_ingress_size; 585 + ret = mxl862xx_evlan_block_alloc(priv, &priv->ports[port].ingress_evlan); 586 + if (ret) 587 + return ret; 588 + 589 + priv->ports[port].egress_evlan.block_size = priv->evlan_egress_size; 590 + ret = mxl862xx_evlan_block_alloc(priv, &priv->ports[port].egress_evlan); 591 + if (ret) 592 + return ret; 593 + 594 + priv->ports[port].vf.block_size = priv->vf_block_size; 595 + INIT_LIST_HEAD(&priv->ports[port].vf.vids); 596 + ret = mxl862xx_vf_alloc(priv, &priv->ports[port].vf); 597 + if (ret) 598 + return ret; 599 + 1344 600 priv->ports[port].setup_done = true; 1345 601 1346 602 return 0; ··· 1367 591 struct mxl862xx_priv *priv = ds->priv; 1368 592 struct dsa_port *dp = dsa_to_port(ds, port); 1369 593 1370 - if (dsa_port_is_unused(dp) || dsa_port_is_dsa(dp)) 594 + if (dsa_port_is_unused(dp)) 1371 595 return; 1372 596 1373 597 /* Prevent deferred host_flood_work from acting on stale state. ··· 1755 979 .port_fdb_dump = mxl862xx_port_fdb_dump, 1756 980 .port_mdb_add = mxl862xx_port_mdb_add, 1757 981 .port_mdb_del = mxl862xx_port_mdb_del, 982 + .port_vlan_filtering = mxl862xx_port_vlan_filtering, 983 + .port_vlan_add = mxl862xx_port_vlan_add, 984 + .port_vlan_del = mxl862xx_port_vlan_del, 1758 985 }; 1759 986 1760 987 static void mxl862xx_phylink_mac_config(struct phylink_config *config,
+92 -11
drivers/net/dsa/mxl862xx/mxl862xx.h
··· 13 13 #define MXL862XX_DEFAULT_BRIDGE 0 14 14 #define MXL862XX_MAX_BRIDGES 48 15 15 #define MXL862XX_MAX_BRIDGE_PORTS 128 16 + #define MXL862XX_TOTAL_EVLAN_ENTRIES 1024 17 + #define MXL862XX_TOTAL_VF_ENTRIES 1024 16 18 17 19 /* Number of __le16 words in a firmware portmap (128-bit bitmap). */ 18 20 #define MXL862XX_FW_PORTMAP_WORDS (MXL862XX_MAX_BRIDGE_PORTS / 16) ··· 57 55 } 58 56 59 57 /** 58 + * struct mxl862xx_vf_vid - Per-VID entry within a VLAN Filter block 59 + * @list: Linked into &mxl862xx_vf_block.vids 60 + * @vid: VLAN ID 61 + * @index: Entry index within the VLAN Filter HW block 62 + * @untagged: Strip tag on egress for this VID (drives EVLAN tag-stripping) 63 + */ 64 + struct mxl862xx_vf_vid { 65 + struct list_head list; 66 + u16 vid; 67 + u16 index; 68 + bool untagged; 69 + }; 70 + 71 + /** 72 + * struct mxl862xx_vf_block - Per-port VLAN Filter block 73 + * @allocated: Whether the HW block has been allocated via VLANFILTER_ALLOC 74 + * @block_id: HW VLAN Filter block ID from VLANFILTER_ALLOC 75 + * @block_size: Total entries allocated in this block 76 + * @active_count: Number of ALLOW entries at indices [0, active_count). 77 + * The bridge port config sends max(active_count, 1) as 78 + * block_size to narrow the HW scan window. 79 + * discard_unmatched_tagged handles frames outside this range. 80 + * @vids: List of &mxl862xx_vf_vid entries programmed in this block 81 + */ 82 + struct mxl862xx_vf_block { 83 + bool allocated; 84 + u16 block_id; 85 + u16 block_size; 86 + u16 active_count; 87 + struct list_head vids; 88 + }; 89 + 90 + /** 91 + * struct mxl862xx_evlan_block - Per-port per-direction extended VLAN block 92 + * @allocated: Whether the HW block has been allocated via EXTENDEDVLAN_ALLOC. 93 + * Guards alloc/free idempotency--the block_id is only valid 94 + * while allocated is true. 95 + * @in_use: Whether the EVLAN engine should be enabled for this block 96 + * on the bridge port (sent as the enable flag in 97 + * set_bridge_port). Can be false while allocated is still 98 + * true -- e.g. when all egress VIDs are removed (idx == 0 in 99 + * evlan_program_egress) the block stays allocated for 100 + * potential reuse, but the engine is disabled so an empty 101 + * rule set does not discard all traffic. 102 + * @block_id: HW block ID from EXTENDEDVLAN_ALLOC 103 + * @block_size: Total entries allocated 104 + * @n_active: Number of HW entries currently written. The bridge port 105 + * config sends this as the egress scan window, so entries 106 + * beyond n_active are never scanned. Always equals 107 + * block_size for ingress blocks (fixed catchall rules). 108 + */ 109 + struct mxl862xx_evlan_block { 110 + bool allocated; 111 + bool in_use; 112 + u16 block_id; 113 + u16 block_size; 114 + u16 n_active; 115 + }; 116 + 117 + /** 60 118 * struct mxl862xx_port - per-port state tracked by the driver 61 119 * @priv: back-pointer to switch private data; needed by 62 120 * deferred work handlers to access ds and priv ··· 130 68 * @setup_done: set at end of port_setup, cleared at start of 131 69 * port_teardown; guards deferred work against 132 70 * acting on torn-down state 71 + * @pvid: port VLAN ID (native VLAN) assigned to untagged traffic 72 + * @vlan_filtering: true when VLAN filtering is enabled on this port 73 + * @vf: per-port VLAN Filter block state 74 + * @ingress_evlan: ingress extended VLAN block state 75 + * @egress_evlan: egress extended VLAN block state 133 76 * @host_flood_uc: desired host unicast flood state (true = flood); 134 77 * updated atomically by port_set_host_flood, consumed 135 78 * by the deferred host_flood_work ··· 152 85 unsigned long flood_block; 153 86 bool learning; 154 87 bool setup_done; 88 + u16 pvid; 89 + bool vlan_filtering; 90 + struct mxl862xx_vf_block vf; 91 + struct mxl862xx_evlan_block ingress_evlan; 92 + struct mxl862xx_evlan_block egress_evlan; 155 93 bool host_flood_uc; 156 94 bool host_flood_mc; 157 95 struct work_struct host_flood_work; ··· 164 92 165 93 /** 166 94 * struct mxl862xx_priv - driver private data for an MxL862xx switch 167 - * @ds: pointer to the DSA switch instance 168 - * @mdiodev: MDIO device used to communicate with the switch firmware 169 - * @crc_err_work: deferred work for shutting down all ports on MDIO CRC errors 170 - * @crc_err: set atomically before CRC-triggered shutdown, cleared after 171 - * @drop_meter: index of the single shared zero-rate firmware meter used 172 - * to unconditionally drop traffic (used to block flooding) 173 - * @ports: per-port state, indexed by switch port number 174 - * @bridges: maps DSA bridge number to firmware bridge ID; 175 - * zero means no firmware bridge allocated for that 176 - * DSA bridge number. Indexed by dsa_bridge.num 177 - * (0 .. ds->max_num_bridges). 95 + * @ds: pointer to the DSA switch instance 96 + * @mdiodev: MDIO device used to communicate with the switch firmware 97 + * @crc_err_work: deferred work for shutting down all ports on MDIO CRC 98 + * errors 99 + * @crc_err: set atomically before CRC-triggered shutdown, cleared 100 + * after 101 + * @drop_meter: index of the single shared zero-rate firmware meter 102 + * used to unconditionally drop traffic (used to block 103 + * flooding) 104 + * @ports: per-port state, indexed by switch port number 105 + * @bridges: maps DSA bridge number to firmware bridge ID; 106 + * zero means no firmware bridge allocated for that 107 + * DSA bridge number. Indexed by dsa_bridge.num 108 + * (0 .. ds->max_num_bridges). 109 + * @evlan_ingress_size: per-port ingress Extended VLAN block size 110 + * @evlan_egress_size: per-port egress Extended VLAN block size 111 + * @vf_block_size: per-port VLAN Filter block size 178 112 */ 179 113 struct mxl862xx_priv { 180 114 struct dsa_switch *ds; ··· 190 112 u16 drop_meter; 191 113 struct mxl862xx_port ports[MXL862XX_MAX_PORTS]; 192 114 u16 bridges[MXL862XX_MAX_BRIDGES + 1]; 115 + u16 evlan_ingress_size; 116 + u16 evlan_egress_size; 117 + u16 vf_block_size; 193 118 }; 194 119 195 120 #endif /* __MXL862XX_H */