| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563 |
- From 0d88d02cc9dccad01ff88f54e1beee867107b942 Mon Sep 17 00:00:00 2001
- From: Daniel Golle <[email protected]>
- Date: Tue, 10 Mar 2026 02:36:00 +0000
- Subject: [PATCH 05/26] net: dsa: mxl862xx: implement VLAN functionality
- Add VLAN support using both the Extended VLAN (EVLAN) engine and the
- VLAN Filter (VF) engine in a hybrid architecture that allows a higher
- number of VIDs than either engine could achieve alone.
- The VLAN Filter engine handles per-port VID membership checks with
- discard-unmatched semantics. The Extended VLAN engine handles PVID
- insertion on ingress (via fixed catchall rules) and tag stripping on
- egress (2 rules per untagged VID). Tagged-only VIDs need no EVLAN
- egress rules at all, so they consume only a VF entry.
- Both engines draw from shared 1024-entry hardware pools. The VF pool
- is divided equally among user ports for VID membership, while the
- EVLAN pool is partitioned into small fixed-size ingress blocks (7
- entries of catchall rules per port) and variable-size egress blocks
- for tag stripping.
- With 5 user ports this yields up to 204 VIDs per port (limited by VF),
- of which up to 98 can be untagged (limited by EVLAN egress budget).
- With 9 user ports the numbers are 113 total and 53 untagged.
- Wire up .port_vlan_add, .port_vlan_del, and .port_vlan_filtering.
- Reprogram all EVLAN rules when the PVID or filtering mode changes.
- Detach blocks from the bridge port before freeing them on bridge leave
- to satisfy the firmware's internal refcount.
- Future optimizations could increase VID capacity by dynamically sizing
- the egress EVLAN blocks based on actual per-port untagged VID counts
- rather than worst-case pre-allocation, or by sharing EVLAN egress and
- VLAN Filter blocks across ports with identical VID sets.
- Signed-off-by: Daniel Golle <[email protected]>
- ---
- drivers/net/dsa/mxl862xx/mxl862xx-api.h | 329 +++++++++
- drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 12 +
- drivers/net/dsa/mxl862xx/mxl862xx.c | 915 +++++++++++++++++++++++-
- drivers/net/dsa/mxl862xx/mxl862xx.h | 104 ++-
- 4 files changed, 1344 insertions(+), 16 deletions(-)
- --- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h
- +++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
- @@ -732,6 +732,335 @@ struct mxl862xx_cfg {
- } __packed;
-
- /**
- + * enum mxl862xx_extended_vlan_filter_type - Extended VLAN filter tag type
- + * @MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL: Normal tagged
- + * @MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER: No filter (wildcard)
- + * @MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT: Default entry
- + * @MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG: Untagged
- + */
- +enum mxl862xx_extended_vlan_filter_type {
- + MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL = 0,
- + MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER = 1,
- + MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT = 2,
- + MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG = 3,
- +};
- +
- +/**
- + * enum mxl862xx_extended_vlan_filter_tpid - Extended VLAN filter TPID
- + * @MXL862XX_EXTENDEDVLAN_FILTER_TPID_NO_FILTER: No TPID filter
- + * @MXL862XX_EXTENDEDVLAN_FILTER_TPID_8021Q: 802.1Q TPID
- + * @MXL862XX_EXTENDEDVLAN_FILTER_TPID_VTETYPE: VLAN type extension
- + */
- +enum mxl862xx_extended_vlan_filter_tpid {
- + MXL862XX_EXTENDEDVLAN_FILTER_TPID_NO_FILTER = 0,
- + MXL862XX_EXTENDEDVLAN_FILTER_TPID_8021Q = 1,
- + MXL862XX_EXTENDEDVLAN_FILTER_TPID_VTETYPE = 2,
- +};
- +
- +/**
- + * enum mxl862xx_extended_vlan_filter_dei - Extended VLAN filter DEI
- + * @MXL862XX_EXTENDEDVLAN_FILTER_DEI_NO_FILTER: No DEI filter
- + * @MXL862XX_EXTENDEDVLAN_FILTER_DEI_0: DEI = 0
- + * @MXL862XX_EXTENDEDVLAN_FILTER_DEI_1: DEI = 1
- + */
- +enum mxl862xx_extended_vlan_filter_dei {
- + MXL862XX_EXTENDEDVLAN_FILTER_DEI_NO_FILTER = 0,
- + MXL862XX_EXTENDEDVLAN_FILTER_DEI_0 = 1,
- + MXL862XX_EXTENDEDVLAN_FILTER_DEI_1 = 2,
- +};
- +
- +/**
- + * enum mxl862xx_extended_vlan_treatment_remove_tag - Tag removal action
- + * @MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG: Do not remove tag
- + * @MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG: Remove one tag
- + * @MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_2_TAG: Remove two tags
- + * @MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM: Discard frame
- + */
- +enum mxl862xx_extended_vlan_treatment_remove_tag {
- + MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG = 0,
- + MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG = 1,
- + MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_2_TAG = 2,
- + MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM = 3,
- +};
- +
- +/**
- + * enum mxl862xx_extended_vlan_treatment_priority - Treatment priority mode
- + * @MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL: Use explicit value
- + * @MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_PRORITY: Copy from inner tag
- + * @MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_PRORITY: Copy from outer tag
- + * @MXL862XX_EXTENDEDVLAN_TREATMENT_DSCP: Derive from DSCP
- + */
- +enum mxl862xx_extended_vlan_treatment_priority {
- + MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL = 0,
- + MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_PRORITY = 1,
- + MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_PRORITY = 2,
- + MXL862XX_EXTENDEDVLAN_TREATMENT_DSCP = 3,
- +};
- +
- +/**
- + * enum mxl862xx_extended_vlan_treatment_vid - Treatment VID mode
- + * @MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL: Use explicit VID value
- + * @MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_VID: Copy from inner tag
- + * @MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_VID: Copy from outer tag
- + */
- +enum mxl862xx_extended_vlan_treatment_vid {
- + MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL = 0,
- + MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_VID = 1,
- + MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_VID = 2,
- +};
- +
- +/**
- + * enum mxl862xx_extended_vlan_treatment_tpid - Treatment TPID mode
- + * @MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_TPID: Copy from inner tag
- + * @MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_TPID: Copy from outer tag
- + * @MXL862XX_EXTENDEDVLAN_TREATMENT_VTETYPE: Use VLAN type extension
- + * @MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q: Use 802.1Q TPID
- + */
- +enum mxl862xx_extended_vlan_treatment_tpid {
- + MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_TPID = 0,
- + MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_TPID = 1,
- + MXL862XX_EXTENDEDVLAN_TREATMENT_VTETYPE = 2,
- + MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q = 3,
- +};
- +
- +/**
- + * enum mxl862xx_extended_vlan_treatment_dei - Treatment DEI mode
- + * @MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_DEI: Copy from inner tag
- + * @MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_DEI: Copy from outer tag
- + * @MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0: Set DEI to 0
- + * @MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_1: Set DEI to 1
- + */
- +enum mxl862xx_extended_vlan_treatment_dei {
- + MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_DEI = 0,
- + MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_DEI = 1,
- + MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0 = 2,
- + MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_1 = 3,
- +};
- +
- +/**
- + * enum mxl862xx_extended_vlan_4_tpid_mode - 4-TPID mode selector
- + * @MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_1: VLAN TPID type 1
- + * @MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_2: VLAN TPID type 2
- + * @MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_3: VLAN TPID type 3
- + * @MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_4: VLAN TPID type 4
- + */
- +enum mxl862xx_extended_vlan_4_tpid_mode {
- + MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_1 = 0,
- + MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_2 = 1,
- + MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_3 = 2,
- + MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_4 = 3,
- +};
- +
- +/**
- + * enum mxl862xx_extended_vlan_filter_ethertype - Filter EtherType match
- + * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_NO_FILTER: No filter
- + * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_IPOE: IPoE
- + * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_PPPOE: PPPoE
- + * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_ARP: ARP
- + * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_IPV6IPOE: IPv6 IPoE
- + * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_EAPOL: EAPOL
- + * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_DHCPV4: DHCPv4
- + * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_DHCPV6: DHCPv6
- + */
- +enum mxl862xx_extended_vlan_filter_ethertype {
- + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_NO_FILTER = 0,
- + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_IPOE = 1,
- + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_PPPOE = 2,
- + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_ARP = 3,
- + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_IPV6IPOE = 4,
- + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_EAPOL = 5,
- + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_DHCPV4 = 6,
- + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_DHCPV6 = 7,
- +};
- +
- +/**
- + * struct mxl862xx_extendedvlan_filter_vlan - Per-tag filter in Extended VLAN
- + * @type: Tag presence/type match (see &enum mxl862xx_extended_vlan_filter_type)
- + * @priority_enable: Enable PCP value matching
- + * @priority_val: PCP value to match
- + * @vid_enable: Enable VID matching
- + * @vid_val: VID value to match
- + * @tpid: TPID match mode (see &enum mxl862xx_extended_vlan_filter_tpid)
- + * @dei: DEI match mode (see &enum mxl862xx_extended_vlan_filter_dei)
- + */
- +struct mxl862xx_extendedvlan_filter_vlan {
- + __le32 type;
- + u8 priority_enable;
- + __le32 priority_val;
- + u8 vid_enable;
- + __le32 vid_val;
- + __le32 tpid;
- + __le32 dei;
- +} __packed;
- +
- +/**
- + * struct mxl862xx_extendedvlan_filter - Extended VLAN filter configuration
- + * @original_packet_filter_mode: If true, filter on original (pre-treatment)
- + * packet
- + * @filter_4_tpid_mode: 4-TPID mode (see &enum mxl862xx_extended_vlan_4_tpid_mode)
- + * @outer_vlan: Outer VLAN tag filter
- + * @inner_vlan: Inner VLAN tag filter
- + * @ether_type: EtherType filter (see
- + * &enum mxl862xx_extended_vlan_filter_ethertype)
- + */
- +struct mxl862xx_extendedvlan_filter {
- + u8 original_packet_filter_mode;
- + __le32 filter_4_tpid_mode;
- + struct mxl862xx_extendedvlan_filter_vlan outer_vlan;
- + struct mxl862xx_extendedvlan_filter_vlan inner_vlan;
- + __le32 ether_type;
- +} __packed;
- +
- +/**
- + * struct mxl862xx_extendedvlan_treatment_vlan - Per-tag treatment in
- + * Extended VLAN
- + * @priority_mode: Priority assignment mode
- + * (see &enum mxl862xx_extended_vlan_treatment_priority)
- + * @priority_val: Priority value (when mode is VAL)
- + * @vid_mode: VID assignment mode
- + * (see &enum mxl862xx_extended_vlan_treatment_vid)
- + * @vid_val: VID value (when mode is VAL)
- + * @tpid: TPID assignment mode
- + * (see &enum mxl862xx_extended_vlan_treatment_tpid)
- + * @dei: DEI assignment mode
- + * (see &enum mxl862xx_extended_vlan_treatment_dei)
- + */
- +struct mxl862xx_extendedvlan_treatment_vlan {
- + __le32 priority_mode;
- + __le32 priority_val;
- + __le32 vid_mode;
- + __le32 vid_val;
- + __le32 tpid;
- + __le32 dei;
- +} __packed;
- +
- +/**
- + * struct mxl862xx_extendedvlan_treatment - Extended VLAN treatment
- + * @remove_tag: Tag removal action
- + * (see &enum mxl862xx_extended_vlan_treatment_remove_tag)
- + * @treatment_4_tpid_mode: 4-TPID treatment mode
- + * @add_outer_vlan: Add outer VLAN tag
- + * @outer_vlan: Outer VLAN tag treatment parameters
- + * @add_inner_vlan: Add inner VLAN tag
- + * @inner_vlan: Inner VLAN tag treatment parameters
- + * @reassign_bridge_port: Reassign to different bridge port
- + * @new_bridge_port_id: New bridge port ID
- + * @new_dscp_enable: Enable new DSCP assignment
- + * @new_dscp: New DSCP value
- + * @new_traffic_class_enable: Enable new traffic class assignment
- + * @new_traffic_class: New traffic class value
- + * @new_meter_enable: Enable new metering
- + * @s_new_traffic_meter_id: New traffic meter ID
- + * @dscp2pcp_map: DSCP to PCP mapping table (64 entries)
- + * @loopback_enable: Enable loopback
- + * @da_sa_swap_enable: Enable DA/SA swap
- + * @mirror_enable: Enable mirroring
- + */
- +struct mxl862xx_extendedvlan_treatment {
- + __le32 remove_tag;
- + __le32 treatment_4_tpid_mode;
- + u8 add_outer_vlan;
- + struct mxl862xx_extendedvlan_treatment_vlan outer_vlan;
- + u8 add_inner_vlan;
- + struct mxl862xx_extendedvlan_treatment_vlan inner_vlan;
- + u8 reassign_bridge_port;
- + __le16 new_bridge_port_id;
- + u8 new_dscp_enable;
- + __le16 new_dscp;
- + u8 new_traffic_class_enable;
- + u8 new_traffic_class;
- + u8 new_meter_enable;
- + __le16 s_new_traffic_meter_id;
- + u8 dscp2pcp_map[64];
- + u8 loopback_enable;
- + u8 da_sa_swap_enable;
- + u8 mirror_enable;
- +} __packed;
- +
- +/**
- + * struct mxl862xx_extendedvlan_alloc - Extended VLAN block allocation
- + * @number_of_entries: Number of entries to allocate (input) / allocated
- + * (output)
- + * @extended_vlan_block_id: Block ID assigned by firmware (output on alloc,
- + * input on free)
- + *
- + * Used with %MXL862XX_EXTENDEDVLAN_ALLOC and %MXL862XX_EXTENDEDVLAN_FREE.
- + */
- +struct mxl862xx_extendedvlan_alloc {
- + __le16 number_of_entries;
- + __le16 extended_vlan_block_id;
- +} __packed;
- +
- +/**
- + * struct mxl862xx_extendedvlan_config - Extended VLAN entry configuration
- + * @extended_vlan_block_id: Block ID from allocation
- + * @entry_index: Entry index within the block
- + * @filter: Filter (match) configuration
- + * @treatment: Treatment (action) configuration
- + *
- + * Used with %MXL862XX_EXTENDEDVLAN_SET and %MXL862XX_EXTENDEDVLAN_GET.
- + */
- +struct mxl862xx_extendedvlan_config {
- + __le16 extended_vlan_block_id;
- + __le16 entry_index;
- + struct mxl862xx_extendedvlan_filter filter;
- + struct mxl862xx_extendedvlan_treatment treatment;
- +} __packed;
- +
- +/**
- + * enum mxl862xx_vlan_filter_tci_mask - VLAN Filter TCI mask
- + * @MXL862XX_VLAN_FILTER_TCI_MASK_VID: TCI mask for VLAN ID
- + * @MXL862XX_VLAN_FILTER_TCI_MASK_PCP: TCI mask for VLAN PCP
- + * @MXL862XX_VLAN_FILTER_TCI_MASK_TCI: TCI mask for VLAN TCI
- + */
- +enum mxl862xx_vlan_filter_tci_mask {
- + MXL862XX_VLAN_FILTER_TCI_MASK_VID = 0,
- + MXL862XX_VLAN_FILTER_TCI_MASK_PCP = 1,
- + MXL862XX_VLAN_FILTER_TCI_MASK_TCI = 2,
- +};
- +
- +/**
- + * struct mxl862xx_vlanfilter_alloc - VLAN Filter block allocation
- + * @number_of_entries: Number of entries to allocate (input) / allocated
- + * (output)
- + * @vlan_filter_block_id: Block ID assigned by firmware (output on alloc,
- + * input on free)
- + * @discard_untagged: Discard untagged packets
- + * @discard_unmatched_tagged: Discard tagged packets that do not match any
- + * entry in the block
- + * @use_default_port_vid: Use default port VLAN ID for filtering
- + *
- + * Used with %MXL862XX_VLANFILTER_ALLOC and %MXL862XX_VLANFILTER_FREE.
- + */
- +struct mxl862xx_vlanfilter_alloc {
- + __le16 number_of_entries;
- + __le16 vlan_filter_block_id;
- + u8 discard_untagged;
- + u8 discard_unmatched_tagged;
- + u8 use_default_port_vid;
- +} __packed;
- +
- +/**
- + * struct mxl862xx_vlanfilter_config - VLAN Filter entry configuration
- + * @vlan_filter_block_id: Block ID from allocation
- + * @entry_index: Entry index within the block
- + * @vlan_filter_mask: TCI field(s) to match (see
- + * &enum mxl862xx_vlan_filter_tci_mask)
- + * @val: TCI value(s) to match (VID, PCP, or full TCI depending on mask)
- + * @discard_matched: When true, discard frames matching this entry;
- + * when false, allow them
- + *
- + * Used with %MXL862XX_VLANFILTER_SET and %MXL862XX_VLANFILTER_GET.
- + */
- +struct mxl862xx_vlanfilter_config {
- + __le16 vlan_filter_block_id;
- + __le16 entry_index;
- + __le32 vlan_filter_mask; /* enum mxl862xx_vlan_filter_tci_mask */
- + __le32 val;
- + u8 discard_matched;
- +} __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
- --- a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
- +++ b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
- @@ -17,6 +17,8 @@
- #define MXL862XX_CTP_MAGIC 0x500
- #define MXL862XX_QOS_MAGIC 0x600
- #define MXL862XX_SWMAC_MAGIC 0xa00
- +#define MXL862XX_EXTVLAN_MAGIC 0xb00
- +#define MXL862XX_VLANFILTER_MAGIC 0xc00
- #define MXL862XX_STP_MAGIC 0xf00
- #define MXL862XX_SS_MAGIC 0x1600
- #define GPY_GPY2XX_MAGIC 0x1800
- @@ -47,6 +49,16 @@
- #define MXL862XX_MAC_TABLEENTRYREMOVE (MXL862XX_SWMAC_MAGIC + 0x5)
- #define MXL862XX_MAC_TABLECLEARCOND (MXL862XX_SWMAC_MAGIC + 0x8)
-
- +#define MXL862XX_EXTENDEDVLAN_ALLOC (MXL862XX_EXTVLAN_MAGIC + 0x1)
- +#define MXL862XX_EXTENDEDVLAN_SET (MXL862XX_EXTVLAN_MAGIC + 0x2)
- +#define MXL862XX_EXTENDEDVLAN_GET (MXL862XX_EXTVLAN_MAGIC + 0x3)
- +#define MXL862XX_EXTENDEDVLAN_FREE (MXL862XX_EXTVLAN_MAGIC + 0x4)
- +
- +#define MXL862XX_VLANFILTER_ALLOC (MXL862XX_VLANFILTER_MAGIC + 0x1)
- +#define MXL862XX_VLANFILTER_SET (MXL862XX_VLANFILTER_MAGIC + 0x2)
- +#define MXL862XX_VLANFILTER_GET (MXL862XX_VLANFILTER_MAGIC + 0x3)
- +#define MXL862XX_VLANFILTER_FREE (MXL862XX_VLANFILTER_MAGIC + 0x4)
- +
- #define MXL862XX_SS_SPTAG_SET (MXL862XX_SS_MAGIC + 0x2)
-
- #define MXL862XX_STP_PORTCFGSET (MXL862XX_STP_MAGIC + 0x2)
- --- a/drivers/net/dsa/mxl862xx/mxl862xx.c
- +++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
- @@ -50,6 +50,88 @@ static const int mxl862xx_flood_meters[]
- MXL862XX_BRIDGE_PORT_EGRESS_METER_BROADCAST,
- };
-
- +enum mxl862xx_evlan_action {
- + EVLAN_ACCEPT, /* pass-through, no tag removal */
- + EVLAN_STRIP_IF_UNTAGGED, /* remove 1 tag if entry's untagged flag set */
- + EVLAN_DISCARD, /* discard upstream */
- + EVLAN_PVID_OR_DISCARD, /* insert PVID tag or discard if no PVID */
- + EVLAN_PVID_OR_PASS, /* insert PVID tag or pass-through */
- + EVLAN_STRIP1_AND_PVID_OR_DISCARD,/* strip 1 tag + insert PVID, or discard */
- +};
- +
- +struct mxl862xx_evlan_rule_desc {
- + u8 outer_type; /* enum mxl862xx_extended_vlan_filter_type */
- + u8 inner_type; /* enum mxl862xx_extended_vlan_filter_type */
- + u8 outer_tpid; /* enum mxl862xx_extended_vlan_filter_tpid */
- + u8 inner_tpid; /* enum mxl862xx_extended_vlan_filter_tpid */
- + bool match_vid; /* true: match on VID from the vid parameter */
- + u8 action; /* enum mxl862xx_evlan_action */
- +};
- +
- +/* Shorthand constants for readability */
- +#define FT_NORMAL MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL
- +#define FT_NO_FILTER MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER
- +#define FT_DEFAULT MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT
- +#define FT_NO_TAG MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG
- +#define TP_NONE MXL862XX_EXTENDEDVLAN_FILTER_TPID_NO_FILTER
- +#define TP_8021Q MXL862XX_EXTENDEDVLAN_FILTER_TPID_8021Q
- +
- +/*
- + * VLAN-aware ingress: 7 final catchall rules.
- + *
- + * VLAN Filter handles VID membership for tagged frames, so the
- + * Extended VLAN ingress block only needs to handle:
- + * - Priority-tagged (VID=0): strip + insert PVID
- + * - Untagged: insert PVID or discard
- + * - Standard 802.1Q VID>0: pass through (VF handles membership)
- + * - Non-8021Q TPID (0x88A8 etc.): treat as untagged
- + *
- + * Rule ordering is critical: the EVLAN engine scans entries in
- + * ascending index order and stops at the first match.
- + *
- + * The 802.1Q ACCEPT rules (indices 3--4) must appear BEFORE the
- + * NO_FILTER catchalls (indices 5--6). NO_FILTER matches any tag
- + * regardless of TPID, so without the ACCEPT guard, it would also
- + * catch standard 802.1Q VID>0 frames and corrupt them. With the
- + * guard, 802.1Q VID>0 frames match the ACCEPT rules first and
- + * pass through untouched; only non-8021Q TPID frames fall through
- + * to the NO_FILTER catchalls.
- + */
- +static const struct mxl862xx_evlan_rule_desc ingress_aware_final[] = {
- + /* 802.1p / priority-tagged (VID 0): strip + PVID */
- + { FT_NORMAL, FT_NORMAL, TP_8021Q, TP_8021Q, true, EVLAN_STRIP1_AND_PVID_OR_DISCARD },
- + { FT_NORMAL, FT_NO_TAG, TP_8021Q, TP_NONE, true, EVLAN_STRIP1_AND_PVID_OR_DISCARD },
- + /* Untagged: PVID insertion or discard */
- + { FT_NO_TAG, FT_NO_TAG, TP_NONE, TP_NONE, false, EVLAN_PVID_OR_DISCARD },
- + /* 802.1Q VID>0: accept - VF handles membership.
- + * match_vid=false means any VID; VID=0 is already caught above.
- + */
- + { FT_NORMAL, FT_NORMAL, TP_8021Q, TP_8021Q, false, EVLAN_ACCEPT },
- + { FT_NORMAL, FT_NO_TAG, TP_8021Q, TP_NONE, false, EVLAN_ACCEPT },
- + /* Non-8021Q TPID (0x88A8 etc.): treat as untagged - strip + PVID */
- + { FT_NO_FILTER, FT_NO_FILTER, TP_NONE, TP_NONE, false, EVLAN_STRIP1_AND_PVID_OR_DISCARD },
- + { FT_NO_FILTER, FT_NO_TAG, TP_NONE, TP_NONE, false, EVLAN_STRIP1_AND_PVID_OR_DISCARD },
- +};
- +
- +/*
- + * VID-specific accept rules (VLAN-aware, standard tag, 2 per VID).
- + * Outer tag carries the VLAN; inner may or may not be present.
- + */
- +static const struct mxl862xx_evlan_rule_desc vid_accept_standard[] = {
- + { FT_NORMAL, FT_NORMAL, TP_8021Q, TP_8021Q, true, EVLAN_STRIP_IF_UNTAGGED },
- + { FT_NORMAL, FT_NO_TAG, TP_8021Q, TP_NONE, true, EVLAN_STRIP_IF_UNTAGGED },
- +};
- +
- +/*
- + * VID-specific accept rules for VLAN-unaware egress.
- + * The HW sees the MxL tag as outer, real VLAN tag as inner.
- + * match on inner VID with outer=NO_FILTER.
- + */
- +static const struct mxl862xx_evlan_rule_desc vid_accept_egress_unaware[] = {
- + { FT_NO_FILTER, FT_NORMAL, TP_NONE, TP_8021Q, true, EVLAN_STRIP_IF_UNTAGGED },
- + { FT_NO_FILTER, FT_NO_TAG, TP_NONE, TP_NONE, true, EVLAN_STRIP_IF_UNTAGGED },
- +};
- +
- static enum dsa_tag_protocol mxl862xx_get_tag_protocol(struct dsa_switch *ds,
- int port,
- enum dsa_tag_protocol m)
- @@ -275,6 +357,7 @@ static int mxl862xx_set_bridge_port(stru
- struct mxl862xx_port *p = &priv->ports[port];
- u16 bridge_id = dp->bridge ?
- priv->bridges[dp->bridge->num] : p->fid;
- + u16 vf_scan;
- bool enable;
- int i, idx;
-
- @@ -283,9 +366,69 @@ static int mxl862xx_set_bridge_port(stru
- 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_EGRESS_SUB_METER);
- + MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_SUB_METER |
- + MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN |
- + MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN |
- + MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN_FILTER |
- + MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN_FILTER1 |
- + MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MAC_LEARNING);
- br_port_cfg.src_mac_learning_disable = !p->learning;
-
- + /* Extended VLAN block assignments.
- + * Ingress: block_size is sent as-is (all entries are finals).
- + * Egress: n_active narrows the scan window to only the
- + * entries actually written by evlan_program_egress.
- + */
- + br_port_cfg.ingress_extended_vlan_enable = p->ingress_evlan.in_use;
- + br_port_cfg.ingress_extended_vlan_block_id =
- + cpu_to_le16(p->ingress_evlan.block_id);
- + br_port_cfg.ingress_extended_vlan_block_size =
- + cpu_to_le16(p->ingress_evlan.block_size);
- + br_port_cfg.egress_extended_vlan_enable = p->egress_evlan.in_use;
- + br_port_cfg.egress_extended_vlan_block_id =
- + cpu_to_le16(p->egress_evlan.block_id);
- + br_port_cfg.egress_extended_vlan_block_size =
- + cpu_to_le16(p->egress_evlan.n_active);
- +
- + /* VLAN Filter block assignments (per-port).
- + * The block_size sent to the firmware narrows the HW scan
- + * window to [block_id, block_id + active_count), relying on
- + * discard_unmatched_tagged for frames outside that range.
- + * When active_count=0, send 1 to scan only the DISCARD
- + * sentinel at index 0 (block_size=0 would disable narrowing
- + * and scan the entire allocated block).
- + *
- + * The bridge check ensures VF is disabled when the port
- + * leaves the bridge, without needing to prematurely clear
- + * vlan_filtering (which the DSA framework handles later via
- + * port_vlan_filtering).
- + */
- + if (p->vf.allocated && p->vlan_filtering &&
- + dsa_port_bridge_dev_get(dp)) {
- + vf_scan = max_t(u16, p->vf.active_count, 1);
- + br_port_cfg.ingress_vlan_filter_enable = 1;
- + br_port_cfg.ingress_vlan_filter_block_id =
- + cpu_to_le16(p->vf.block_id);
- + br_port_cfg.ingress_vlan_filter_block_size =
- + cpu_to_le16(vf_scan);
- +
- + br_port_cfg.egress_vlan_filter1enable = 1;
- + br_port_cfg.egress_vlan_filter1block_id =
- + cpu_to_le16(p->vf.block_id);
- + br_port_cfg.egress_vlan_filter1block_size =
- + cpu_to_le16(vf_scan);
- + } else {
- + br_port_cfg.ingress_vlan_filter_enable = 0;
- + br_port_cfg.egress_vlan_filter1enable = 0;
- + }
- +
- + /* IVL when VLAN-aware: include VID in FDB lookup keys so that
- + * learned entries are per-VID. In VLAN-unaware mode, SVL is
- + * used (VID excluded from key).
- + */
- + br_port_cfg.vlan_src_mac_vid_enable = p->vlan_filtering;
- + br_port_cfg.vlan_dst_mac_vid_enable = p->vlan_filtering;
- +
- mxl862xx_fw_portmap_from_bitmap(br_port_cfg.bridge_port_map, p->portmap);
-
- for (i = 0; i < ARRAY_SIZE(mxl862xx_flood_meters); i++) {
- @@ -329,6 +472,91 @@ static int mxl862xx_sync_bridge_members(
- return ret;
- }
-
- +static void mxl862xx_evlan_block_init(struct mxl862xx_evlan_block *blk,
- + u16 size)
- +{
- + blk->allocated = false;
- + blk->in_use = false;
- + blk->block_id = 0;
- + blk->block_size = size;
- + blk->n_active = 0;
- +}
- +
- +static int mxl862xx_evlan_block_alloc(struct mxl862xx_priv *priv,
- + struct mxl862xx_evlan_block *blk)
- +{
- + struct mxl862xx_extendedvlan_alloc param = {};
- + int ret;
- +
- + param.number_of_entries = cpu_to_le16(blk->block_size);
- +
- + ret = MXL862XX_API_READ(priv, MXL862XX_EXTENDEDVLAN_ALLOC, param);
- + if (ret)
- + return ret;
- +
- + blk->block_id = le16_to_cpu(param.extended_vlan_block_id);
- + blk->allocated = true;
- +
- + return 0;
- +}
- +
- +static void mxl862xx_vf_init(struct mxl862xx_vf_block *vf, u16 size)
- +{
- + vf->allocated = false;
- + vf->block_id = 0;
- + vf->block_size = size;
- + vf->active_count = 0;
- + INIT_LIST_HEAD(&vf->vids);
- +}
- +
- +static int mxl862xx_vf_block_alloc(struct mxl862xx_priv *priv,
- + u16 size, u16 *block_id)
- +{
- + struct mxl862xx_vlanfilter_alloc param = {};
- + int ret;
- +
- + param.number_of_entries = cpu_to_le16(size);
- + param.discard_untagged = 0;
- + param.discard_unmatched_tagged = 1;
- +
- + ret = MXL862XX_API_READ(priv, MXL862XX_VLANFILTER_ALLOC, param);
- + if (ret)
- + return ret;
- +
- + *block_id = le16_to_cpu(param.vlan_filter_block_id);
- + return 0;
- +}
- +
- +static int mxl862xx_vf_entry_discard(struct mxl862xx_priv *priv,
- + u16 block_id, u16 index)
- +{
- + struct mxl862xx_vlanfilter_config cfg = {};
- +
- + cfg.vlan_filter_block_id = cpu_to_le16(block_id);
- + cfg.entry_index = cpu_to_le16(index);
- + cfg.vlan_filter_mask = cpu_to_le32(MXL862XX_VLAN_FILTER_TCI_MASK_VID);
- + cfg.val = cpu_to_le32(0);
- + cfg.discard_matched = 1;
- +
- + return MXL862XX_API_WRITE(priv, MXL862XX_VLANFILTER_SET, cfg);
- +}
- +
- +static int mxl862xx_vf_alloc(struct mxl862xx_priv *priv,
- + struct mxl862xx_vf_block *vf)
- +{
- + int ret;
- +
- + ret = mxl862xx_vf_block_alloc(priv, vf->block_size, &vf->block_id);
- + if (ret)
- + return ret;
- +
- + vf->allocated = true;
- + vf->active_count = 0;
- +
- + /* Sentinel: block VID-0 when scan window covers only index 0 */
- + return mxl862xx_vf_entry_discard(priv, vf->block_id, 0);
- +}
- +
- static int mxl862xx_allocate_bridge(struct mxl862xx_priv *priv, u16 *bridge_id)
- {
- struct mxl862xx_bridge_alloc br_alloc = {};
- @@ -392,6 +620,9 @@ static int mxl862xx_add_single_port_brid
- static int mxl862xx_setup(struct dsa_switch *ds)
- {
- struct mxl862xx_priv *priv = ds->priv;
- + int n_user_ports = 0, max_vlans;
- + int ingress_finals, vid_rules;
- + struct dsa_port *dp;
- int ret;
-
- ret = mxl862xx_reset(priv);
- @@ -402,6 +633,50 @@ static int mxl862xx_setup(struct dsa_swi
- if (ret)
- return ret;
-
- + /* Calculate Extended VLAN block sizes.
- + * With VLAN Filter handling VID membership checks:
- + * Ingress: only final catchall rules (PVID insertion, 802.1Q
- + * accept, non-8021Q TPID handling, discard).
- + * Block sized to exactly fit the finals -- no per-VID
- + * ingress EVLAN rules are needed. (7 entries.)
- + * Egress: 2 rules per VID that needs tag stripping (untagged VIDs).
- + * No egress final catchalls -- VLAN Filter does the discard.
- + * CPU: EVLAN is left disabled on CPU ports -- frames pass
- + * through without EVLAN processing.
- + *
- + * Total EVLAN budget:
- + * n_user_ports * (ingress + egress) ≤ 1024.
- + * Ingress blocks are small (7 entries), so almost all capacity
- + * goes to egress VID rules.
- + */
- + dsa_switch_for_each_user_port(dp, ds)
- + n_user_ports++;
- +
- + if (n_user_ports) {
- + ingress_finals = ARRAY_SIZE(ingress_aware_final);
- + vid_rules = ARRAY_SIZE(vid_accept_standard);
- +
- + /* Ingress block: fixed at finals count (7 entries) */
- + priv->evlan_ingress_size = ingress_finals;
- +
- + /* Egress block: remaining budget divided equally among
- + * user ports. Each untagged VID needs vid_rules (2)
- + * EVLAN entries for tag stripping. Tagged-only VIDs
- + * need no EVLAN rules at all.
- + */
- + max_vlans = (MXL862XX_TOTAL_EVLAN_ENTRIES -
- + n_user_ports * ingress_finals) /
- + (n_user_ports * vid_rules);
- + priv->evlan_egress_size = vid_rules * max_vlans;
- +
- + /* VLAN Filter block: one per user port. The 1024-entry
- + * table is divided equally among user ports. Each port
- + * gets its own VF block for per-port VID membership --
- + * discard_unmatched_tagged handles the rest.
- + */
- + priv->vf_block_size = MXL862XX_TOTAL_VF_ENTRIES / n_user_ports;
- + }
- +
- ret = mxl862xx_setup_drop_meter(ds);
- if (ret)
- return ret;
- @@ -483,27 +758,616 @@ static int mxl862xx_configure_sp_tag_pro
- return MXL862XX_API_WRITE(ds->priv, MXL862XX_SS_SPTAG_SET, tag);
- }
-
- +/**
- + * mxl862xx_evlan_write_rule - Write a single Extended VLAN rule to hardware
- + * @priv: driver private data
- + * @block_id: HW Extended VLAN block ID
- + * @entry_index: entry index within the block
- + * @desc: rule descriptor (filter type + action)
- + * @vid: VLAN ID for VID-specific rules (ignored when !desc->match_vid)
- + * @untagged: strip tag on egress for EVLAN_STRIP_IF_UNTAGGED action
- + * @pvid: port VLAN ID for PVID insertion rules (0 = no PVID)
- + *
- + * Translates a compact rule descriptor into a full firmware
- + * mxl862xx_extendedvlan_config struct and writes it via the API.
- + */
- +static int mxl862xx_evlan_write_rule(struct mxl862xx_priv *priv,
- + u16 block_id, u16 entry_index,
- + const struct mxl862xx_evlan_rule_desc *desc,
- + u16 vid, bool untagged, u16 pvid)
- +{
- + struct mxl862xx_extendedvlan_config cfg = {};
- + struct mxl862xx_extendedvlan_filter_vlan *fv;
- +
- + cfg.extended_vlan_block_id = cpu_to_le16(block_id);
- + cfg.entry_index = cpu_to_le16(entry_index);
- +
- + /* Populate filter */
- + cfg.filter.outer_vlan.type = cpu_to_le32(desc->outer_type);
- + cfg.filter.inner_vlan.type = cpu_to_le32(desc->inner_type);
- + cfg.filter.outer_vlan.tpid = cpu_to_le32(desc->outer_tpid);
- + cfg.filter.inner_vlan.tpid = cpu_to_le32(desc->inner_tpid);
- +
- + if (desc->match_vid) {
- + /* For egress unaware: outer=NO_FILTER, match on inner tag */
- + if (desc->outer_type == FT_NO_FILTER)
- + fv = &cfg.filter.inner_vlan;
- + else
- + fv = &cfg.filter.outer_vlan;
- +
- + fv->vid_enable = 1;
- + fv->vid_val = cpu_to_le32(vid);
- + }
- +
- + /* Populate treatment based on action */
- + switch (desc->action) {
- + case EVLAN_ACCEPT:
- + cfg.treatment.remove_tag =
- + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG);
- + break;
- +
- + case EVLAN_STRIP_IF_UNTAGGED:
- + cfg.treatment.remove_tag = cpu_to_le32(untagged ?
- + MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG :
- + MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG);
- + break;
- +
- + case EVLAN_DISCARD:
- + cfg.treatment.remove_tag =
- + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM);
- + break;
- +
- + case EVLAN_PVID_OR_DISCARD:
- + if (pvid) {
- + cfg.treatment.remove_tag =
- + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG);
- + cfg.treatment.add_outer_vlan = 1;
- + cfg.treatment.outer_vlan.vid_mode =
- + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL);
- + cfg.treatment.outer_vlan.vid_val = cpu_to_le32(pvid);
- + cfg.treatment.outer_vlan.tpid =
- + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q);
- + } else {
- + cfg.treatment.remove_tag =
- + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM);
- + }
- + break;
- +
- + case EVLAN_PVID_OR_PASS:
- + if (pvid) {
- + cfg.treatment.remove_tag =
- + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG);
- + cfg.treatment.add_outer_vlan = 1;
- + cfg.treatment.outer_vlan.vid_mode =
- + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL);
- + cfg.treatment.outer_vlan.vid_val = cpu_to_le32(pvid);
- + cfg.treatment.outer_vlan.tpid =
- + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q);
- + } else {
- + cfg.treatment.remove_tag =
- + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG);
- + }
- + break;
- +
- + case EVLAN_STRIP1_AND_PVID_OR_DISCARD:
- + if (pvid) {
- + cfg.treatment.remove_tag =
- + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG);
- + cfg.treatment.add_outer_vlan = 1;
- + cfg.treatment.outer_vlan.vid_mode =
- + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL);
- + cfg.treatment.outer_vlan.vid_val = cpu_to_le32(pvid);
- + cfg.treatment.outer_vlan.tpid =
- + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q);
- + } else {
- + cfg.treatment.remove_tag =
- + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM);
- + }
- + break;
- + }
- +
- + return MXL862XX_API_WRITE(priv, MXL862XX_EXTENDEDVLAN_SET, cfg);
- +}
- +
- +/**
- + * mxl862xx_evlan_deactivate_entry - Reset an Extended VLAN entry to no-op
- + * @priv: driver private data
- + * @block_id: HW Extended VLAN block ID
- + * @entry_index: entry index within the block
- + *
- + * Writes a zeroed-out config to the firmware, which deactivates the
- + * rule (making it transparent / no-op).
- + */
- +static int mxl862xx_evlan_deactivate_entry(struct mxl862xx_priv *priv,
- + u16 block_id, u16 entry_index)
- +{
- + struct mxl862xx_extendedvlan_config cfg = {};
- +
- + cfg.extended_vlan_block_id = cpu_to_le16(block_id);
- + cfg.entry_index = cpu_to_le16(entry_index);
- +
- + /* Use an unreachable filter (DEFAULT+DEFAULT) with DISCARD treatment.
- + * A zeroed entry would have NORMAL+NORMAL filter which matches
- + * real double-tagged traffic and passes it through.
- + */
- + cfg.filter.outer_vlan.type =
- + cpu_to_le32(MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT);
- + cfg.filter.inner_vlan.type =
- + cpu_to_le32(MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT);
- + cfg.treatment.remove_tag =
- + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM);
- +
- + return MXL862XX_API_WRITE(priv, MXL862XX_EXTENDEDVLAN_SET, cfg);
- +}
- +
- +/**
- + * mxl862xx_evlan_write_final_rules - Write catchall rules to the ingress block
- + * @priv: driver private data
- + * @blk: Extended VLAN block (already allocated)
- + * @rules: array of rule descriptors for the final rules
- + * @n_rules: number of final rules
- + * @pvid: port VLAN ID (for PVID insertion rules)
- + *
- + * Writes final catchall rules starting at block_size - n_rules. With
- + * VLAN Filter handling VID membership, only the ingress block uses
- + * finals, and the block is sized to exactly fit them (no VID entries),
- + * so the rules fill the entire block.
- + */
- +static int mxl862xx_evlan_write_final_rules(struct mxl862xx_priv *priv,
- + struct mxl862xx_evlan_block *blk,
- + const struct mxl862xx_evlan_rule_desc *rules,
- + int n_rules, u16 pvid)
- +{
- + u16 start_idx = blk->block_size - n_rules;
- + int i, ret;
- +
- + for (i = 0; i < n_rules; i++) {
- + ret = mxl862xx_evlan_write_rule(priv, blk->block_id,
- + start_idx + i, &rules[i],
- + 0, false, pvid);
- + if (ret)
- + return ret;
- + }
- +
- + return 0;
- +}
- +
- +/**
- + * mxl862xx_vf_entry_set - Write a single VLAN Filter entry
- + * @priv: driver private data
- + * @block_id: HW VLAN Filter block ID
- + * @index: entry index within the block
- + * @vid: VLAN ID to allow
- + *
- + * Writes an ALLOW entry (discard_matched=false) for the given VID.
- + */
- +static int mxl862xx_vf_entry_set(struct mxl862xx_priv *priv,
- + u16 block_id, u16 index, u16 vid)
- +{
- + struct mxl862xx_vlanfilter_config cfg = {};
- +
- + cfg.vlan_filter_block_id = cpu_to_le16(block_id);
- + cfg.entry_index = cpu_to_le16(index);
- + cfg.vlan_filter_mask = cpu_to_le32(MXL862XX_VLAN_FILTER_TCI_MASK_VID);
- + cfg.val = cpu_to_le32(vid);
- + cfg.discard_matched = 0;
- +
- + return MXL862XX_API_WRITE(priv, MXL862XX_VLANFILTER_SET, cfg);
- +}
- +
- +/**
- + * mxl862xx_vf_find_vid - Find a VID entry in a VF block
- + * @vf: VLAN Filter block to search
- + * @vid: VLAN ID to find
- + */
- +static struct mxl862xx_vf_vid *
- +mxl862xx_vf_find_vid(struct mxl862xx_vf_block *vf, u16 vid)
- +{
- + struct mxl862xx_vf_vid *ve;
- +
- + list_for_each_entry(ve, &vf->vids, list)
- + if (ve->vid == vid)
- + return ve;
- +
- + return NULL;
- +}
- +
- +/**
- + * mxl862xx_vf_add_vid - Add a VID to a port's VLAN Filter block
- + * @priv: driver private data
- + * @vf: VLAN Filter block
- + * @vid: VLAN ID to add
- + * @untagged: whether this VID should strip tags on egress
- + *
- + * Idempotent. Writes an ALLOW entry at active_count and increments
- + * active_count. If the VID already exists, only the untagged flag
- + * is updated. The HW block must be allocated before calling this.
- + */
- +static int mxl862xx_vf_add_vid(struct mxl862xx_priv *priv,
- + struct mxl862xx_vf_block *vf,
- + u16 vid, bool untagged)
- +{
- + struct mxl862xx_vf_vid *ve;
- + int ret;
- +
- + ve = mxl862xx_vf_find_vid(vf, vid);
- + if (ve) {
- + ve->untagged = untagged;
- + return 0;
- + }
- +
- + if (vf->active_count >= vf->block_size)
- + return -ENOSPC;
- +
- + ve = kzalloc(sizeof(*ve), GFP_KERNEL);
- + if (!ve)
- + return -ENOMEM;
- +
- + ve->vid = vid;
- + ve->index = vf->active_count;
- + ve->untagged = untagged;
- +
- + ret = mxl862xx_vf_entry_set(priv, vf->block_id, ve->index, vid);
- + if (ret) {
- + kfree(ve);
- + return ret;
- + }
- +
- + list_add_tail(&ve->list, &vf->vids);
- + vf->active_count++;
- +
- + return 0;
- +}
- +
- +/**
- + * mxl862xx_vf_del_vid - Remove a VID from a port's VLAN Filter block
- + * @priv: driver private data
- + * @vf: VLAN Filter block
- + * @vid: VLAN ID to remove
- + *
- + * Swap-compacts: the last active entry is moved into the gap,
- + * active_count is decremented, and the old last slot is plugged
- + * with DISCARD. When active_count drops to 0, a DISCARD sentinel
- + * is restored at index 0.
- + */
- +static int mxl862xx_vf_del_vid(struct mxl862xx_priv *priv,
- + struct mxl862xx_vf_block *vf, u16 vid)
- +{
- + struct mxl862xx_vf_vid *ve, *last_ve;
- + u16 gap, last;
- + int ret;
- +
- + ve = mxl862xx_vf_find_vid(vf, vid);
- + if (!ve)
- + return 0;
- +
- + if (!vf->allocated) {
- + /* Software-only state -- just remove the tracking entry */
- + list_del(&ve->list);
- + kfree(ve);
- + vf->active_count--;
- + return 0;
- + }
- +
- + gap = ve->index;
- + last = vf->active_count - 1;
- +
- + if (vf->active_count == 1) {
- + /* Last VID -- restore DISCARD sentinel at index 0 */
- + ret = mxl862xx_vf_entry_discard(priv, vf->block_id, 0);
- + if (ret)
- + return ret;
- + } else if (gap < last) {
- + /* Swap: move the last ALLOW entry into the gap */
- + last_ve = NULL;
- + list_for_each_entry(last_ve, &vf->vids, list)
- + if (last_ve->index == last)
- + break;
- +
- + if (WARN_ON(!last_ve || last_ve->index != last))
- + return -EINVAL;
- +
- + ret = mxl862xx_vf_entry_set(priv, vf->block_id,
- + gap, last_ve->vid);
- + if (ret)
- + return ret;
- +
- + last_ve->index = gap;
- +
- + /* Plug the old last slot with DISCARD */
- + ret = mxl862xx_vf_entry_discard(priv, vf->block_id, last);
- + if (ret)
- + return ret;
- + } else {
- + /* Deleting the last entry -- just plug it */
- + ret = mxl862xx_vf_entry_discard(priv, vf->block_id, last);
- + if (ret)
- + return ret;
- + }
- +
- + list_del(&ve->list);
- + kfree(ve);
- + vf->active_count--;
- +
- + return 0;
- +}
- +
- +/**
- + * mxl862xx_evlan_program_ingress - Write the fixed ingress catchall rules
- + * @priv: driver private data
- + * @port: port number
- + *
- + * In VLAN-aware mode the ingress EVLAN block handles PVID insertion for
- + * untagged/priority-tagged frames, passes through standard 802.1Q
- + * tagged frames for VF membership checking, and treats non-8021Q TPID
- + * frames as untagged. The block is sized to exactly fit the 7 catchall
- + * rules and is rewritten whenever PVID changes.
- + *
- + * In VLAN-unaware mode the firmware passes frames through unchanged when
- + * no ingress block is assigned, so nothing is programmed.
- + */
- +static int mxl862xx_evlan_program_ingress(struct mxl862xx_priv *priv, int port)
- +{
- + struct mxl862xx_port *p = &priv->ports[port];
- + struct mxl862xx_evlan_block *blk = &p->ingress_evlan;
- +
- + if (!p->vlan_filtering)
- + return 0;
- +
- + blk->in_use = true;
- + blk->n_active = blk->block_size;
- +
- + return mxl862xx_evlan_write_final_rules(priv, blk,
- + ingress_aware_final,
- + ARRAY_SIZE(ingress_aware_final),
- + p->pvid);
- +}
- +
- +/**
- + * mxl862xx_evlan_program_egress - Reprogram all egress tag-stripping rules
- + * @priv: driver private data
- + * @port: port number
- + *
- + * Walks the port's VF VID list and writes 2 EVLAN rules per VID that
- + * needs egress tag stripping. In VLAN-aware mode only untagged VIDs
- + * need rules (tagged VIDs pass through EVLAN untouched). In unaware
- + * mode every VID gets rules.
- + *
- + * Entries are packed starting at index 0, and the scan window
- + * (n_active) is narrowed so stale entries beyond it are never matched.
- + */
- +static int mxl862xx_evlan_program_egress(struct mxl862xx_priv *priv, int port)
- +{
- + struct mxl862xx_port *p = &priv->ports[port];
- + struct mxl862xx_evlan_block *blk = &p->egress_evlan;
- + const struct mxl862xx_evlan_rule_desc *vid_rules;
- + struct mxl862xx_vf_vid *vfv;
- + u16 old_active = blk->n_active;
- + u16 idx = 0, i;
- + int n_vid, ret;
- +
- + if (p->vlan_filtering) {
- + vid_rules = vid_accept_standard;
- + n_vid = ARRAY_SIZE(vid_accept_standard);
- + } else {
- + vid_rules = vid_accept_egress_unaware;
- + n_vid = ARRAY_SIZE(vid_accept_egress_unaware);
- + }
- +
- + list_for_each_entry(vfv, &p->vf.vids, list) {
- + /* In VLAN-aware mode tagged-only VIDs need no EVLAN
- + * rules -- VLAN Filter handles membership.
- + */
- + if (p->vlan_filtering && !vfv->untagged)
- + continue;
- +
- + if (idx + n_vid > blk->block_size)
- + return -ENOSPC;
- +
- + ret = mxl862xx_evlan_write_rule(priv, blk->block_id,
- + idx++, &vid_rules[0],
- + vfv->vid, vfv->untagged,
- + p->pvid);
- + if (ret)
- + return ret;
- +
- + if (n_vid > 1) {
- + ret = mxl862xx_evlan_write_rule(priv, blk->block_id,
- + idx++, &vid_rules[1],
- + vfv->vid,
- + vfv->untagged,
- + p->pvid);
- + if (ret)
- + return ret;
- + }
- + }
- +
- + /* Deactivate stale entries that are no longer needed.
- + * This closes the brief window between writing the new rules
- + * and set_bridge_port narrowing the scan window.
- + */
- + for (i = idx; i < old_active; i++) {
- + ret = mxl862xx_evlan_deactivate_entry(priv,
- + blk->block_id,
- + i);
- + if (ret)
- + return ret;
- + }
- +
- + blk->n_active = idx;
- + blk->in_use = idx > 0;
- +
- + return 0;
- +}
- +
- +static int mxl862xx_port_vlan_filtering(struct dsa_switch *ds, int port,
- + bool vlan_filtering,
- + struct netlink_ext_ack *extack)
- +{
- + struct mxl862xx_priv *priv = ds->priv;
- + struct mxl862xx_port *p = &priv->ports[port];
- + bool changed = (p->vlan_filtering != vlan_filtering);
- + int ret;
- +
- + p->vlan_filtering = vlan_filtering;
- +
- + /* Reprogram Extended VLAN rules if filtering mode changed */
- + if (changed) {
- + /* When leaving VLAN-aware mode, release the ingress HW
- + * block. The firmware passes frames through unchanged
- + * when no ingress EVLAN block is assigned, so the block
- + * is unnecessary in unaware mode.
- + */
- + if (!vlan_filtering)
- + p->ingress_evlan.in_use = false;
- +
- + ret = mxl862xx_evlan_program_ingress(priv, port);
- + if (ret)
- + return ret;
- +
- + ret = mxl862xx_evlan_program_egress(priv, port);
- + if (ret)
- + return ret;
- + }
- +
- + /* Push VLAN-based MAC learning flags and (possibly newly
- + * allocated) ingress block to hardware.
- + */
- + return mxl862xx_set_bridge_port(ds, port);
- +}
- +
- +static int mxl862xx_port_vlan_add(struct dsa_switch *ds, int port,
- + const struct switchdev_obj_port_vlan *vlan,
- + struct netlink_ext_ack *extack)
- +{
- + struct mxl862xx_priv *priv = ds->priv;
- + struct mxl862xx_port *p = &priv->ports[port];
- + bool untagged = !!(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED);
- + u16 vid = vlan->vid;
- + u16 old_pvid = p->pvid;
- + bool pvid_changed = false;
- + int ret;
- +
- + /* CPU port is VLAN-transparent: the SP tag handles port
- + * identification and the host-side DSA tagger manages VLAN
- + * delivery. Egress EVLAN catchalls are set up once in
- + * setup_cpu_bridge; no per-VID VF/EVLAN programming needed.
- + */
- + if (dsa_is_cpu_port(ds, port))
- + return 0;
- +
- + /* Update PVID tracking */
- + if (vlan->flags & BRIDGE_VLAN_INFO_PVID) {
- + if (p->pvid != vid) {
- + p->pvid = vid;
- + pvid_changed = true;
- + }
- + } else if (p->pvid == vid) {
- + p->pvid = 0;
- + pvid_changed = true;
- + }
- +
- + /* Add/update VID in this port's VLAN Filter block.
- + * VF must be updated before programming egress EVLAN because
- + * evlan_program_egress walks the VF VID list.
- + */
- + ret = mxl862xx_vf_add_vid(priv, &p->vf, vid, untagged);
- + if (ret)
- + goto err_pvid;
- +
- + /* Reprogram ingress finals if PVID changed */
- + if (pvid_changed) {
- + ret = mxl862xx_evlan_program_ingress(priv, port);
- + if (ret)
- + goto err_pvid;
- + }
- +
- + /* Reprogram egress tag-stripping rules (walks VF VID list) */
- + ret = mxl862xx_evlan_program_egress(priv, port);
- + if (ret)
- + goto err_pvid;
- +
- + /* Apply VLAN block IDs and MAC learning flags to bridge port */
- + ret = mxl862xx_set_bridge_port(ds, port);
- + if (ret)
- + goto err_pvid;
- +
- + return 0;
- +
- +err_pvid:
- + p->pvid = old_pvid;
- + return ret;
- +}
- +
- +static int mxl862xx_port_vlan_del(struct dsa_switch *ds, int port,
- + const struct switchdev_obj_port_vlan *vlan)
- +{
- + struct mxl862xx_priv *priv = ds->priv;
- + struct mxl862xx_port *p = &priv->ports[port];
- + u16 vid = vlan->vid;
- + bool pvid_changed = false;
- + int ret;
- +
- + if (dsa_is_cpu_port(ds, port))
- + return 0;
- +
- + /* Clear PVID if we're deleting it */
- + if (p->pvid == vid) {
- + p->pvid = 0;
- + pvid_changed = true;
- + }
- +
- + /* Remove VID from this port's VLAN Filter block.
- + * Must happen before egress reprogram so the VID is no
- + * longer in the list that evlan_program_egress walks.
- + */
- + ret = mxl862xx_vf_del_vid(priv, &p->vf, vid);
- + if (ret)
- + return ret;
- +
- + /* Reprogram egress tag-stripping rules (VID is now gone) */
- + ret = mxl862xx_evlan_program_egress(priv, port);
- + if (ret)
- + return ret;
- +
- + /* If PVID changed, reprogram ingress finals */
- + if (pvid_changed) {
- + ret = mxl862xx_evlan_program_ingress(priv, port);
- + if (ret)
- + return ret;
- + }
- +
- + return mxl862xx_set_bridge_port(ds, port);
- +}
- +
- static int mxl862xx_setup_cpu_bridge(struct dsa_switch *ds, int port)
- {
- struct mxl862xx_priv *priv = ds->priv;
- + struct mxl862xx_port *p = &priv->ports[port];
- struct dsa_port *dp;
-
- - priv->ports[port].fid = MXL862XX_DEFAULT_BRIDGE;
- - priv->ports[port].learning = true;
- + p->fid = MXL862XX_DEFAULT_BRIDGE;
- + p->learning = true;
- +
- + /* EVLAN is left disabled on CPU ports -- frames pass through
- + * without EVLAN processing. Only the portmap and bridge
- + * assignment need to be configured.
- + */
-
- /* include all assigned user ports in the CPU portmap */
- - bitmap_zero(priv->ports[port].portmap, MXL862XX_MAX_BRIDGE_PORTS);
- + bitmap_zero(p->portmap, MXL862XX_MAX_BRIDGE_PORTS);
- 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;
-
- - __set_bit(dp->index, priv->ports[port].portmap);
- + __set_bit(dp->index, p->portmap);
- }
-
- return mxl862xx_set_bridge_port(ds, port);
- }
-
- +
- static int mxl862xx_port_bridge_join(struct dsa_switch *ds, int port,
- const struct dsa_bridge bridge,
- bool *tx_fwd_offload,
- @@ -553,6 +1417,22 @@ static void mxl862xx_port_bridge_leave(s
- bitmap_zero(p->portmap, MXL862XX_MAX_BRIDGE_PORTS);
- __set_bit(dp->cpu_dp->index, p->portmap);
- p->flood_block = 0;
- +
- + /* Detach EVLAN and VF blocks from the bridge port BEFORE freeing
- + * them. The firmware tracks a usage count per block and rejects
- + * FREE while the count is non-zero.
- + *
- + * For EVLAN: setting in_use=false makes set_bridge_port send
- + * enable=false, which decrements the firmware refcount.
- + *
- + * For VF: set_bridge_port sees dp->bridge == NULL (DSA already
- + * cleared it) and sends vlan_filter_enable=0, which decrements
- + * the firmware VF refcount.
- + */
- + p->pvid = 0;
- + p->ingress_evlan.in_use = false;
- + p->egress_evlan.in_use = false;
- +
- err = mxl862xx_set_bridge_port(ds, port);
- if (err)
- dev_err(ds->dev,
- @@ -602,6 +1482,28 @@ static int mxl862xx_port_setup(struct ds
- if (ret)
- return ret;
-
- + /* Initialize and pre-allocate per-port EVLAN and VF blocks for
- + * user ports. CPU ports do not use EVLAN or VF -- frames pass
- + * through without processing. Pre-allocation avoids firmware
- + * EVLAN table fragmentation and simplifies control flow.
- + */
- + mxl862xx_evlan_block_init(&priv->ports[port].ingress_evlan,
- + priv->evlan_ingress_size);
- + ret = mxl862xx_evlan_block_alloc(priv, &priv->ports[port].ingress_evlan);
- + if (ret)
- + return ret;
- +
- + mxl862xx_evlan_block_init(&priv->ports[port].egress_evlan,
- + priv->evlan_egress_size);
- + ret = mxl862xx_evlan_block_alloc(priv, &priv->ports[port].egress_evlan);
- + if (ret)
- + return ret;
- +
- + mxl862xx_vf_init(&priv->ports[port].vf, priv->vf_block_size);
- + ret = mxl862xx_vf_alloc(priv, &priv->ports[port].vf);
- + if (ret)
- + return ret;
- +
- priv->ports[port].setup_done = true;
-
- return 0;
- @@ -1012,6 +1914,9 @@ static const struct dsa_switch_ops mxl86
- .port_fdb_dump = mxl862xx_port_fdb_dump,
- .port_mdb_add = mxl862xx_port_mdb_add,
- .port_mdb_del = mxl862xx_port_mdb_del,
- + .port_vlan_filtering = mxl862xx_port_vlan_filtering,
- + .port_vlan_add = mxl862xx_port_vlan_add,
- + .port_vlan_del = mxl862xx_port_vlan_del,
- };
-
- static void mxl862xx_phylink_mac_config(struct phylink_config *config,
- --- a/drivers/net/dsa/mxl862xx/mxl862xx.h
- +++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
- @@ -13,6 +13,8 @@ struct mxl862xx_priv;
- #define MXL862XX_DEFAULT_BRIDGE 0
- #define MXL862XX_MAX_BRIDGES 48
- #define MXL862XX_MAX_BRIDGE_PORTS 128
- +#define MXL862XX_TOTAL_EVLAN_ENTRIES 1024
- +#define MXL862XX_TOTAL_VF_ENTRIES 1024
-
- /* Number of __le16 words in a firmware portmap (128-bit bitmap). */
- #define MXL862XX_FW_PORTMAP_WORDS (MXL862XX_MAX_BRIDGE_PORTS / 16)
- @@ -86,6 +88,66 @@ static inline bool mxl862xx_fw_portmap_i
- }
-
- /**
- + * struct mxl862xx_vf_vid - Per-VID entry within a VLAN Filter block
- + * @list: Linked into &mxl862xx_vf_block.vids
- + * @vid: VLAN ID
- + * @index: Entry index within the VLAN Filter HW block
- + * @untagged: Strip tag on egress for this VID (drives EVLAN tag-stripping)
- + */
- +struct mxl862xx_vf_vid {
- + struct list_head list;
- + u16 vid;
- + u16 index;
- + bool untagged;
- +};
- +
- +/**
- + * struct mxl862xx_vf_block - Per-port VLAN Filter block
- + * @allocated: Whether the HW block has been allocated via VLANFILTER_ALLOC
- + * @block_id: HW VLAN Filter block ID from VLANFILTER_ALLOC
- + * @block_size: Total entries allocated in this block
- + * @active_count: Number of ALLOW entries at indices [0, active_count).
- + * The bridge port config sends max(active_count, 1) as
- + * block_size to narrow the HW scan window.
- + * discard_unmatched_tagged handles frames outside this range.
- + * @vids: List of &mxl862xx_vf_vid entries programmed in this block
- + */
- +struct mxl862xx_vf_block {
- + bool allocated;
- + u16 block_id;
- + u16 block_size;
- + u16 active_count;
- + struct list_head vids;
- +};
- +
- +/**
- + * struct mxl862xx_evlan_block - Per-port per-direction extended VLAN block
- + * @allocated: Whether the HW block has been allocated via EXTENDEDVLAN_ALLOC.
- + * Guards alloc/free idempotency--the block_id is only valid
- + * while allocated is true.
- + * @in_use: Whether the EVLAN engine should be enabled for this block
- + * on the bridge port (sent as the enable flag in
- + * set_bridge_port). Can be false while allocated is still
- + * true -- e.g. when all egress VIDs are removed (idx == 0 in
- + * evlan_program_egress) the block stays allocated for
- + * potential reuse, but the engine is disabled so an empty
- + * rule set does not discard all traffic.
- + * @block_id: HW block ID from EXTENDEDVLAN_ALLOC
- + * @block_size: Total entries allocated
- + * @n_active: Number of HW entries currently written. The bridge port
- + * config sends this as the egress scan window, so entries
- + * beyond n_active are never scanned. Always equals
- + * block_size for ingress blocks (fixed catchall rules).
- + */
- +struct mxl862xx_evlan_block {
- + bool allocated;
- + bool in_use;
- + u16 block_id;
- + u16 block_size;
- + u16 n_active;
- +};
- +
- +/**
- * struct mxl862xx_port - per-port state tracked by the driver
- * @priv: back-pointer to switch private data; needed by
- * deferred work handlers to access ds and priv
- @@ -101,6 +163,11 @@ static inline bool mxl862xx_fw_portmap_i
- * @setup_done: set at end of port_setup, cleared at start of
- * port_teardown; guards deferred work against
- * acting on torn-down state
- + * @pvid: port VLAN ID (native VLAN) assigned to untagged traffic
- + * @vlan_filtering: true when VLAN filtering is enabled on this port
- + * @vf: per-port VLAN Filter block state
- + * @ingress_evlan: ingress extended VLAN block state
- + * @egress_evlan: egress extended VLAN block state
- * @host_flood_uc: desired host unicast flood state (true = flood);
- * updated atomically by port_set_host_flood, consumed
- * by the deferred host_flood_work
- @@ -119,6 +186,12 @@ struct mxl862xx_port {
- unsigned long flood_block;
- bool learning;
- bool setup_done;
- + /* VLAN state */
- + u16 pvid;
- + bool vlan_filtering;
- + struct mxl862xx_vf_block vf;
- + struct mxl862xx_evlan_block ingress_evlan;
- + struct mxl862xx_evlan_block egress_evlan;
- bool host_flood_uc;
- bool host_flood_mc;
- struct work_struct host_flood_work;
- @@ -126,17 +199,23 @@ struct mxl862xx_port {
-
- /**
- * struct mxl862xx_priv - driver private data for an MxL862xx switch
- - * @ds: pointer to the DSA switch instance
- - * @mdiodev: MDIO device used to communicate with the switch firmware
- - * @crc_err_work: deferred work for shutting down all ports on MDIO CRC errors
- - * @crc_err: set atomically before CRC-triggered shutdown, cleared after
- - * @drop_meter: index of the single shared zero-rate firmware meter used
- - * to unconditionally drop traffic (used to block flooding)
- - * @ports: per-port state, indexed by switch port number
- - * @bridges: maps DSA bridge number to firmware bridge ID;
- - * zero means no firmware bridge allocated for that
- - * DSA bridge number. Indexed by dsa_bridge.num
- - * (0 .. ds->max_num_bridges).
- + * @ds: pointer to the DSA switch instance
- + * @mdiodev: MDIO device used to communicate with the switch firmware
- + * @crc_err_work: deferred work for shutting down all ports on MDIO CRC
- + * errors
- + * @crc_err: set atomically before CRC-triggered shutdown, cleared
- + * after
- + * @drop_meter: index of the single shared zero-rate firmware meter
- + * used to unconditionally drop traffic (used to block
- + * flooding)
- + * @ports: per-port state, indexed by switch port number
- + * @bridges: maps DSA bridge number to firmware bridge ID;
- + * zero means no firmware bridge allocated for that
- + * DSA bridge number. Indexed by dsa_bridge.num
- + * (0 .. ds->max_num_bridges).
- + * @evlan_ingress_size: per-port ingress Extended VLAN block size
- + * @evlan_egress_size: per-port egress Extended VLAN block size
- + * @vf_block_size: per-port VLAN Filter block size
- */
- struct mxl862xx_priv {
- struct dsa_switch *ds;
- @@ -146,6 +225,9 @@ struct mxl862xx_priv {
- u16 drop_meter;
- struct mxl862xx_port ports[MXL862XX_MAX_PORTS];
- u16 bridges[MXL862XX_MAX_BRIDGES + 1];
- + u16 evlan_ingress_size;
- + u16 evlan_egress_size;
- + u16 vf_block_size;
- };
-
- #endif /* __MXL862XX_H */
|