760-05-net-dsa-mxl862xx-implement-VLAN-functionality.patch 54 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563
  1. From 0d88d02cc9dccad01ff88f54e1beee867107b942 Mon Sep 17 00:00:00 2001
  2. From: Daniel Golle <[email protected]>
  3. Date: Tue, 10 Mar 2026 02:36:00 +0000
  4. Subject: [PATCH 05/26] net: dsa: mxl862xx: implement VLAN functionality
  5. Add VLAN support using both the Extended VLAN (EVLAN) engine and the
  6. VLAN Filter (VF) engine in a hybrid architecture that allows a higher
  7. number of VIDs than either engine could achieve alone.
  8. The VLAN Filter engine handles per-port VID membership checks with
  9. discard-unmatched semantics. The Extended VLAN engine handles PVID
  10. insertion on ingress (via fixed catchall rules) and tag stripping on
  11. egress (2 rules per untagged VID). Tagged-only VIDs need no EVLAN
  12. egress rules at all, so they consume only a VF entry.
  13. Both engines draw from shared 1024-entry hardware pools. The VF pool
  14. is divided equally among user ports for VID membership, while the
  15. EVLAN pool is partitioned into small fixed-size ingress blocks (7
  16. entries of catchall rules per port) and variable-size egress blocks
  17. for tag stripping.
  18. With 5 user ports this yields up to 204 VIDs per port (limited by VF),
  19. of which up to 98 can be untagged (limited by EVLAN egress budget).
  20. With 9 user ports the numbers are 113 total and 53 untagged.
  21. Wire up .port_vlan_add, .port_vlan_del, and .port_vlan_filtering.
  22. Reprogram all EVLAN rules when the PVID or filtering mode changes.
  23. Detach blocks from the bridge port before freeing them on bridge leave
  24. to satisfy the firmware's internal refcount.
  25. Future optimizations could increase VID capacity by dynamically sizing
  26. the egress EVLAN blocks based on actual per-port untagged VID counts
  27. rather than worst-case pre-allocation, or by sharing EVLAN egress and
  28. VLAN Filter blocks across ports with identical VID sets.
  29. Signed-off-by: Daniel Golle <[email protected]>
  30. ---
  31. drivers/net/dsa/mxl862xx/mxl862xx-api.h | 329 +++++++++
  32. drivers/net/dsa/mxl862xx/mxl862xx-cmd.h | 12 +
  33. drivers/net/dsa/mxl862xx/mxl862xx.c | 915 +++++++++++++++++++++++-
  34. drivers/net/dsa/mxl862xx/mxl862xx.h | 104 ++-
  35. 4 files changed, 1344 insertions(+), 16 deletions(-)
  36. --- a/drivers/net/dsa/mxl862xx/mxl862xx-api.h
  37. +++ b/drivers/net/dsa/mxl862xx/mxl862xx-api.h
  38. @@ -732,6 +732,335 @@ struct mxl862xx_cfg {
  39. } __packed;
  40. /**
  41. + * enum mxl862xx_extended_vlan_filter_type - Extended VLAN filter tag type
  42. + * @MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL: Normal tagged
  43. + * @MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER: No filter (wildcard)
  44. + * @MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT: Default entry
  45. + * @MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG: Untagged
  46. + */
  47. +enum mxl862xx_extended_vlan_filter_type {
  48. + MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL = 0,
  49. + MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER = 1,
  50. + MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT = 2,
  51. + MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG = 3,
  52. +};
  53. +
  54. +/**
  55. + * enum mxl862xx_extended_vlan_filter_tpid - Extended VLAN filter TPID
  56. + * @MXL862XX_EXTENDEDVLAN_FILTER_TPID_NO_FILTER: No TPID filter
  57. + * @MXL862XX_EXTENDEDVLAN_FILTER_TPID_8021Q: 802.1Q TPID
  58. + * @MXL862XX_EXTENDEDVLAN_FILTER_TPID_VTETYPE: VLAN type extension
  59. + */
  60. +enum mxl862xx_extended_vlan_filter_tpid {
  61. + MXL862XX_EXTENDEDVLAN_FILTER_TPID_NO_FILTER = 0,
  62. + MXL862XX_EXTENDEDVLAN_FILTER_TPID_8021Q = 1,
  63. + MXL862XX_EXTENDEDVLAN_FILTER_TPID_VTETYPE = 2,
  64. +};
  65. +
  66. +/**
  67. + * enum mxl862xx_extended_vlan_filter_dei - Extended VLAN filter DEI
  68. + * @MXL862XX_EXTENDEDVLAN_FILTER_DEI_NO_FILTER: No DEI filter
  69. + * @MXL862XX_EXTENDEDVLAN_FILTER_DEI_0: DEI = 0
  70. + * @MXL862XX_EXTENDEDVLAN_FILTER_DEI_1: DEI = 1
  71. + */
  72. +enum mxl862xx_extended_vlan_filter_dei {
  73. + MXL862XX_EXTENDEDVLAN_FILTER_DEI_NO_FILTER = 0,
  74. + MXL862XX_EXTENDEDVLAN_FILTER_DEI_0 = 1,
  75. + MXL862XX_EXTENDEDVLAN_FILTER_DEI_1 = 2,
  76. +};
  77. +
  78. +/**
  79. + * enum mxl862xx_extended_vlan_treatment_remove_tag - Tag removal action
  80. + * @MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG: Do not remove tag
  81. + * @MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG: Remove one tag
  82. + * @MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_2_TAG: Remove two tags
  83. + * @MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM: Discard frame
  84. + */
  85. +enum mxl862xx_extended_vlan_treatment_remove_tag {
  86. + MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG = 0,
  87. + MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG = 1,
  88. + MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_2_TAG = 2,
  89. + MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM = 3,
  90. +};
  91. +
  92. +/**
  93. + * enum mxl862xx_extended_vlan_treatment_priority - Treatment priority mode
  94. + * @MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL: Use explicit value
  95. + * @MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_PRORITY: Copy from inner tag
  96. + * @MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_PRORITY: Copy from outer tag
  97. + * @MXL862XX_EXTENDEDVLAN_TREATMENT_DSCP: Derive from DSCP
  98. + */
  99. +enum mxl862xx_extended_vlan_treatment_priority {
  100. + MXL862XX_EXTENDEDVLAN_TREATMENT_PRIORITY_VAL = 0,
  101. + MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_PRORITY = 1,
  102. + MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_PRORITY = 2,
  103. + MXL862XX_EXTENDEDVLAN_TREATMENT_DSCP = 3,
  104. +};
  105. +
  106. +/**
  107. + * enum mxl862xx_extended_vlan_treatment_vid - Treatment VID mode
  108. + * @MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL: Use explicit VID value
  109. + * @MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_VID: Copy from inner tag
  110. + * @MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_VID: Copy from outer tag
  111. + */
  112. +enum mxl862xx_extended_vlan_treatment_vid {
  113. + MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL = 0,
  114. + MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_VID = 1,
  115. + MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_VID = 2,
  116. +};
  117. +
  118. +/**
  119. + * enum mxl862xx_extended_vlan_treatment_tpid - Treatment TPID mode
  120. + * @MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_TPID: Copy from inner tag
  121. + * @MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_TPID: Copy from outer tag
  122. + * @MXL862XX_EXTENDEDVLAN_TREATMENT_VTETYPE: Use VLAN type extension
  123. + * @MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q: Use 802.1Q TPID
  124. + */
  125. +enum mxl862xx_extended_vlan_treatment_tpid {
  126. + MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_TPID = 0,
  127. + MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_TPID = 1,
  128. + MXL862XX_EXTENDEDVLAN_TREATMENT_VTETYPE = 2,
  129. + MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q = 3,
  130. +};
  131. +
  132. +/**
  133. + * enum mxl862xx_extended_vlan_treatment_dei - Treatment DEI mode
  134. + * @MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_DEI: Copy from inner tag
  135. + * @MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_DEI: Copy from outer tag
  136. + * @MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0: Set DEI to 0
  137. + * @MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_1: Set DEI to 1
  138. + */
  139. +enum mxl862xx_extended_vlan_treatment_dei {
  140. + MXL862XX_EXTENDEDVLAN_TREATMENT_INNER_DEI = 0,
  141. + MXL862XX_EXTENDEDVLAN_TREATMENT_OUTER_DEI = 1,
  142. + MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_0 = 2,
  143. + MXL862XX_EXTENDEDVLAN_TREATMENT_DEI_1 = 3,
  144. +};
  145. +
  146. +/**
  147. + * enum mxl862xx_extended_vlan_4_tpid_mode - 4-TPID mode selector
  148. + * @MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_1: VLAN TPID type 1
  149. + * @MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_2: VLAN TPID type 2
  150. + * @MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_3: VLAN TPID type 3
  151. + * @MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_4: VLAN TPID type 4
  152. + */
  153. +enum mxl862xx_extended_vlan_4_tpid_mode {
  154. + MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_1 = 0,
  155. + MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_2 = 1,
  156. + MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_3 = 2,
  157. + MXL862XX_EXTENDEDVLAN_TPID_VTETYPE_4 = 3,
  158. +};
  159. +
  160. +/**
  161. + * enum mxl862xx_extended_vlan_filter_ethertype - Filter EtherType match
  162. + * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_NO_FILTER: No filter
  163. + * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_IPOE: IPoE
  164. + * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_PPPOE: PPPoE
  165. + * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_ARP: ARP
  166. + * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_IPV6IPOE: IPv6 IPoE
  167. + * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_EAPOL: EAPOL
  168. + * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_DHCPV4: DHCPv4
  169. + * @MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_DHCPV6: DHCPv6
  170. + */
  171. +enum mxl862xx_extended_vlan_filter_ethertype {
  172. + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_NO_FILTER = 0,
  173. + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_IPOE = 1,
  174. + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_PPPOE = 2,
  175. + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_ARP = 3,
  176. + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_IPV6IPOE = 4,
  177. + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_EAPOL = 5,
  178. + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_DHCPV4 = 6,
  179. + MXL862XX_EXTENDEDVLAN_FILTER_ETHERTYPE_DHCPV6 = 7,
  180. +};
  181. +
  182. +/**
  183. + * struct mxl862xx_extendedvlan_filter_vlan - Per-tag filter in Extended VLAN
  184. + * @type: Tag presence/type match (see &enum mxl862xx_extended_vlan_filter_type)
  185. + * @priority_enable: Enable PCP value matching
  186. + * @priority_val: PCP value to match
  187. + * @vid_enable: Enable VID matching
  188. + * @vid_val: VID value to match
  189. + * @tpid: TPID match mode (see &enum mxl862xx_extended_vlan_filter_tpid)
  190. + * @dei: DEI match mode (see &enum mxl862xx_extended_vlan_filter_dei)
  191. + */
  192. +struct mxl862xx_extendedvlan_filter_vlan {
  193. + __le32 type;
  194. + u8 priority_enable;
  195. + __le32 priority_val;
  196. + u8 vid_enable;
  197. + __le32 vid_val;
  198. + __le32 tpid;
  199. + __le32 dei;
  200. +} __packed;
  201. +
  202. +/**
  203. + * struct mxl862xx_extendedvlan_filter - Extended VLAN filter configuration
  204. + * @original_packet_filter_mode: If true, filter on original (pre-treatment)
  205. + * packet
  206. + * @filter_4_tpid_mode: 4-TPID mode (see &enum mxl862xx_extended_vlan_4_tpid_mode)
  207. + * @outer_vlan: Outer VLAN tag filter
  208. + * @inner_vlan: Inner VLAN tag filter
  209. + * @ether_type: EtherType filter (see
  210. + * &enum mxl862xx_extended_vlan_filter_ethertype)
  211. + */
  212. +struct mxl862xx_extendedvlan_filter {
  213. + u8 original_packet_filter_mode;
  214. + __le32 filter_4_tpid_mode;
  215. + struct mxl862xx_extendedvlan_filter_vlan outer_vlan;
  216. + struct mxl862xx_extendedvlan_filter_vlan inner_vlan;
  217. + __le32 ether_type;
  218. +} __packed;
  219. +
  220. +/**
  221. + * struct mxl862xx_extendedvlan_treatment_vlan - Per-tag treatment in
  222. + * Extended VLAN
  223. + * @priority_mode: Priority assignment mode
  224. + * (see &enum mxl862xx_extended_vlan_treatment_priority)
  225. + * @priority_val: Priority value (when mode is VAL)
  226. + * @vid_mode: VID assignment mode
  227. + * (see &enum mxl862xx_extended_vlan_treatment_vid)
  228. + * @vid_val: VID value (when mode is VAL)
  229. + * @tpid: TPID assignment mode
  230. + * (see &enum mxl862xx_extended_vlan_treatment_tpid)
  231. + * @dei: DEI assignment mode
  232. + * (see &enum mxl862xx_extended_vlan_treatment_dei)
  233. + */
  234. +struct mxl862xx_extendedvlan_treatment_vlan {
  235. + __le32 priority_mode;
  236. + __le32 priority_val;
  237. + __le32 vid_mode;
  238. + __le32 vid_val;
  239. + __le32 tpid;
  240. + __le32 dei;
  241. +} __packed;
  242. +
  243. +/**
  244. + * struct mxl862xx_extendedvlan_treatment - Extended VLAN treatment
  245. + * @remove_tag: Tag removal action
  246. + * (see &enum mxl862xx_extended_vlan_treatment_remove_tag)
  247. + * @treatment_4_tpid_mode: 4-TPID treatment mode
  248. + * @add_outer_vlan: Add outer VLAN tag
  249. + * @outer_vlan: Outer VLAN tag treatment parameters
  250. + * @add_inner_vlan: Add inner VLAN tag
  251. + * @inner_vlan: Inner VLAN tag treatment parameters
  252. + * @reassign_bridge_port: Reassign to different bridge port
  253. + * @new_bridge_port_id: New bridge port ID
  254. + * @new_dscp_enable: Enable new DSCP assignment
  255. + * @new_dscp: New DSCP value
  256. + * @new_traffic_class_enable: Enable new traffic class assignment
  257. + * @new_traffic_class: New traffic class value
  258. + * @new_meter_enable: Enable new metering
  259. + * @s_new_traffic_meter_id: New traffic meter ID
  260. + * @dscp2pcp_map: DSCP to PCP mapping table (64 entries)
  261. + * @loopback_enable: Enable loopback
  262. + * @da_sa_swap_enable: Enable DA/SA swap
  263. + * @mirror_enable: Enable mirroring
  264. + */
  265. +struct mxl862xx_extendedvlan_treatment {
  266. + __le32 remove_tag;
  267. + __le32 treatment_4_tpid_mode;
  268. + u8 add_outer_vlan;
  269. + struct mxl862xx_extendedvlan_treatment_vlan outer_vlan;
  270. + u8 add_inner_vlan;
  271. + struct mxl862xx_extendedvlan_treatment_vlan inner_vlan;
  272. + u8 reassign_bridge_port;
  273. + __le16 new_bridge_port_id;
  274. + u8 new_dscp_enable;
  275. + __le16 new_dscp;
  276. + u8 new_traffic_class_enable;
  277. + u8 new_traffic_class;
  278. + u8 new_meter_enable;
  279. + __le16 s_new_traffic_meter_id;
  280. + u8 dscp2pcp_map[64];
  281. + u8 loopback_enable;
  282. + u8 da_sa_swap_enable;
  283. + u8 mirror_enable;
  284. +} __packed;
  285. +
  286. +/**
  287. + * struct mxl862xx_extendedvlan_alloc - Extended VLAN block allocation
  288. + * @number_of_entries: Number of entries to allocate (input) / allocated
  289. + * (output)
  290. + * @extended_vlan_block_id: Block ID assigned by firmware (output on alloc,
  291. + * input on free)
  292. + *
  293. + * Used with %MXL862XX_EXTENDEDVLAN_ALLOC and %MXL862XX_EXTENDEDVLAN_FREE.
  294. + */
  295. +struct mxl862xx_extendedvlan_alloc {
  296. + __le16 number_of_entries;
  297. + __le16 extended_vlan_block_id;
  298. +} __packed;
  299. +
  300. +/**
  301. + * struct mxl862xx_extendedvlan_config - Extended VLAN entry configuration
  302. + * @extended_vlan_block_id: Block ID from allocation
  303. + * @entry_index: Entry index within the block
  304. + * @filter: Filter (match) configuration
  305. + * @treatment: Treatment (action) configuration
  306. + *
  307. + * Used with %MXL862XX_EXTENDEDVLAN_SET and %MXL862XX_EXTENDEDVLAN_GET.
  308. + */
  309. +struct mxl862xx_extendedvlan_config {
  310. + __le16 extended_vlan_block_id;
  311. + __le16 entry_index;
  312. + struct mxl862xx_extendedvlan_filter filter;
  313. + struct mxl862xx_extendedvlan_treatment treatment;
  314. +} __packed;
  315. +
  316. +/**
  317. + * enum mxl862xx_vlan_filter_tci_mask - VLAN Filter TCI mask
  318. + * @MXL862XX_VLAN_FILTER_TCI_MASK_VID: TCI mask for VLAN ID
  319. + * @MXL862XX_VLAN_FILTER_TCI_MASK_PCP: TCI mask for VLAN PCP
  320. + * @MXL862XX_VLAN_FILTER_TCI_MASK_TCI: TCI mask for VLAN TCI
  321. + */
  322. +enum mxl862xx_vlan_filter_tci_mask {
  323. + MXL862XX_VLAN_FILTER_TCI_MASK_VID = 0,
  324. + MXL862XX_VLAN_FILTER_TCI_MASK_PCP = 1,
  325. + MXL862XX_VLAN_FILTER_TCI_MASK_TCI = 2,
  326. +};
  327. +
  328. +/**
  329. + * struct mxl862xx_vlanfilter_alloc - VLAN Filter block allocation
  330. + * @number_of_entries: Number of entries to allocate (input) / allocated
  331. + * (output)
  332. + * @vlan_filter_block_id: Block ID assigned by firmware (output on alloc,
  333. + * input on free)
  334. + * @discard_untagged: Discard untagged packets
  335. + * @discard_unmatched_tagged: Discard tagged packets that do not match any
  336. + * entry in the block
  337. + * @use_default_port_vid: Use default port VLAN ID for filtering
  338. + *
  339. + * Used with %MXL862XX_VLANFILTER_ALLOC and %MXL862XX_VLANFILTER_FREE.
  340. + */
  341. +struct mxl862xx_vlanfilter_alloc {
  342. + __le16 number_of_entries;
  343. + __le16 vlan_filter_block_id;
  344. + u8 discard_untagged;
  345. + u8 discard_unmatched_tagged;
  346. + u8 use_default_port_vid;
  347. +} __packed;
  348. +
  349. +/**
  350. + * struct mxl862xx_vlanfilter_config - VLAN Filter entry configuration
  351. + * @vlan_filter_block_id: Block ID from allocation
  352. + * @entry_index: Entry index within the block
  353. + * @vlan_filter_mask: TCI field(s) to match (see
  354. + * &enum mxl862xx_vlan_filter_tci_mask)
  355. + * @val: TCI value(s) to match (VID, PCP, or full TCI depending on mask)
  356. + * @discard_matched: When true, discard frames matching this entry;
  357. + * when false, allow them
  358. + *
  359. + * Used with %MXL862XX_VLANFILTER_SET and %MXL862XX_VLANFILTER_GET.
  360. + */
  361. +struct mxl862xx_vlanfilter_config {
  362. + __le16 vlan_filter_block_id;
  363. + __le16 entry_index;
  364. + __le32 vlan_filter_mask; /* enum mxl862xx_vlan_filter_tci_mask */
  365. + __le32 val;
  366. + u8 discard_matched;
  367. +} __packed;
  368. +
  369. +/**
  370. * enum mxl862xx_ss_sp_tag_mask - Special tag valid field indicator bits
  371. * @MXL862XX_SS_SP_TAG_MASK_RX: valid RX special tag mode
  372. * @MXL862XX_SS_SP_TAG_MASK_TX: valid TX special tag mode
  373. --- a/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
  374. +++ b/drivers/net/dsa/mxl862xx/mxl862xx-cmd.h
  375. @@ -17,6 +17,8 @@
  376. #define MXL862XX_CTP_MAGIC 0x500
  377. #define MXL862XX_QOS_MAGIC 0x600
  378. #define MXL862XX_SWMAC_MAGIC 0xa00
  379. +#define MXL862XX_EXTVLAN_MAGIC 0xb00
  380. +#define MXL862XX_VLANFILTER_MAGIC 0xc00
  381. #define MXL862XX_STP_MAGIC 0xf00
  382. #define MXL862XX_SS_MAGIC 0x1600
  383. #define GPY_GPY2XX_MAGIC 0x1800
  384. @@ -47,6 +49,16 @@
  385. #define MXL862XX_MAC_TABLEENTRYREMOVE (MXL862XX_SWMAC_MAGIC + 0x5)
  386. #define MXL862XX_MAC_TABLECLEARCOND (MXL862XX_SWMAC_MAGIC + 0x8)
  387. +#define MXL862XX_EXTENDEDVLAN_ALLOC (MXL862XX_EXTVLAN_MAGIC + 0x1)
  388. +#define MXL862XX_EXTENDEDVLAN_SET (MXL862XX_EXTVLAN_MAGIC + 0x2)
  389. +#define MXL862XX_EXTENDEDVLAN_GET (MXL862XX_EXTVLAN_MAGIC + 0x3)
  390. +#define MXL862XX_EXTENDEDVLAN_FREE (MXL862XX_EXTVLAN_MAGIC + 0x4)
  391. +
  392. +#define MXL862XX_VLANFILTER_ALLOC (MXL862XX_VLANFILTER_MAGIC + 0x1)
  393. +#define MXL862XX_VLANFILTER_SET (MXL862XX_VLANFILTER_MAGIC + 0x2)
  394. +#define MXL862XX_VLANFILTER_GET (MXL862XX_VLANFILTER_MAGIC + 0x3)
  395. +#define MXL862XX_VLANFILTER_FREE (MXL862XX_VLANFILTER_MAGIC + 0x4)
  396. +
  397. #define MXL862XX_SS_SPTAG_SET (MXL862XX_SS_MAGIC + 0x2)
  398. #define MXL862XX_STP_PORTCFGSET (MXL862XX_STP_MAGIC + 0x2)
  399. --- a/drivers/net/dsa/mxl862xx/mxl862xx.c
  400. +++ b/drivers/net/dsa/mxl862xx/mxl862xx.c
  401. @@ -50,6 +50,88 @@ static const int mxl862xx_flood_meters[]
  402. MXL862XX_BRIDGE_PORT_EGRESS_METER_BROADCAST,
  403. };
  404. +enum mxl862xx_evlan_action {
  405. + EVLAN_ACCEPT, /* pass-through, no tag removal */
  406. + EVLAN_STRIP_IF_UNTAGGED, /* remove 1 tag if entry's untagged flag set */
  407. + EVLAN_DISCARD, /* discard upstream */
  408. + EVLAN_PVID_OR_DISCARD, /* insert PVID tag or discard if no PVID */
  409. + EVLAN_PVID_OR_PASS, /* insert PVID tag or pass-through */
  410. + EVLAN_STRIP1_AND_PVID_OR_DISCARD,/* strip 1 tag + insert PVID, or discard */
  411. +};
  412. +
  413. +struct mxl862xx_evlan_rule_desc {
  414. + u8 outer_type; /* enum mxl862xx_extended_vlan_filter_type */
  415. + u8 inner_type; /* enum mxl862xx_extended_vlan_filter_type */
  416. + u8 outer_tpid; /* enum mxl862xx_extended_vlan_filter_tpid */
  417. + u8 inner_tpid; /* enum mxl862xx_extended_vlan_filter_tpid */
  418. + bool match_vid; /* true: match on VID from the vid parameter */
  419. + u8 action; /* enum mxl862xx_evlan_action */
  420. +};
  421. +
  422. +/* Shorthand constants for readability */
  423. +#define FT_NORMAL MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NORMAL
  424. +#define FT_NO_FILTER MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_FILTER
  425. +#define FT_DEFAULT MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT
  426. +#define FT_NO_TAG MXL862XX_EXTENDEDVLAN_FILTER_TYPE_NO_TAG
  427. +#define TP_NONE MXL862XX_EXTENDEDVLAN_FILTER_TPID_NO_FILTER
  428. +#define TP_8021Q MXL862XX_EXTENDEDVLAN_FILTER_TPID_8021Q
  429. +
  430. +/*
  431. + * VLAN-aware ingress: 7 final catchall rules.
  432. + *
  433. + * VLAN Filter handles VID membership for tagged frames, so the
  434. + * Extended VLAN ingress block only needs to handle:
  435. + * - Priority-tagged (VID=0): strip + insert PVID
  436. + * - Untagged: insert PVID or discard
  437. + * - Standard 802.1Q VID>0: pass through (VF handles membership)
  438. + * - Non-8021Q TPID (0x88A8 etc.): treat as untagged
  439. + *
  440. + * Rule ordering is critical: the EVLAN engine scans entries in
  441. + * ascending index order and stops at the first match.
  442. + *
  443. + * The 802.1Q ACCEPT rules (indices 3--4) must appear BEFORE the
  444. + * NO_FILTER catchalls (indices 5--6). NO_FILTER matches any tag
  445. + * regardless of TPID, so without the ACCEPT guard, it would also
  446. + * catch standard 802.1Q VID>0 frames and corrupt them. With the
  447. + * guard, 802.1Q VID>0 frames match the ACCEPT rules first and
  448. + * pass through untouched; only non-8021Q TPID frames fall through
  449. + * to the NO_FILTER catchalls.
  450. + */
  451. +static const struct mxl862xx_evlan_rule_desc ingress_aware_final[] = {
  452. + /* 802.1p / priority-tagged (VID 0): strip + PVID */
  453. + { FT_NORMAL, FT_NORMAL, TP_8021Q, TP_8021Q, true, EVLAN_STRIP1_AND_PVID_OR_DISCARD },
  454. + { FT_NORMAL, FT_NO_TAG, TP_8021Q, TP_NONE, true, EVLAN_STRIP1_AND_PVID_OR_DISCARD },
  455. + /* Untagged: PVID insertion or discard */
  456. + { FT_NO_TAG, FT_NO_TAG, TP_NONE, TP_NONE, false, EVLAN_PVID_OR_DISCARD },
  457. + /* 802.1Q VID>0: accept - VF handles membership.
  458. + * match_vid=false means any VID; VID=0 is already caught above.
  459. + */
  460. + { FT_NORMAL, FT_NORMAL, TP_8021Q, TP_8021Q, false, EVLAN_ACCEPT },
  461. + { FT_NORMAL, FT_NO_TAG, TP_8021Q, TP_NONE, false, EVLAN_ACCEPT },
  462. + /* Non-8021Q TPID (0x88A8 etc.): treat as untagged - strip + PVID */
  463. + { FT_NO_FILTER, FT_NO_FILTER, TP_NONE, TP_NONE, false, EVLAN_STRIP1_AND_PVID_OR_DISCARD },
  464. + { FT_NO_FILTER, FT_NO_TAG, TP_NONE, TP_NONE, false, EVLAN_STRIP1_AND_PVID_OR_DISCARD },
  465. +};
  466. +
  467. +/*
  468. + * VID-specific accept rules (VLAN-aware, standard tag, 2 per VID).
  469. + * Outer tag carries the VLAN; inner may or may not be present.
  470. + */
  471. +static const struct mxl862xx_evlan_rule_desc vid_accept_standard[] = {
  472. + { FT_NORMAL, FT_NORMAL, TP_8021Q, TP_8021Q, true, EVLAN_STRIP_IF_UNTAGGED },
  473. + { FT_NORMAL, FT_NO_TAG, TP_8021Q, TP_NONE, true, EVLAN_STRIP_IF_UNTAGGED },
  474. +};
  475. +
  476. +/*
  477. + * VID-specific accept rules for VLAN-unaware egress.
  478. + * The HW sees the MxL tag as outer, real VLAN tag as inner.
  479. + * match on inner VID with outer=NO_FILTER.
  480. + */
  481. +static const struct mxl862xx_evlan_rule_desc vid_accept_egress_unaware[] = {
  482. + { FT_NO_FILTER, FT_NORMAL, TP_NONE, TP_8021Q, true, EVLAN_STRIP_IF_UNTAGGED },
  483. + { FT_NO_FILTER, FT_NO_TAG, TP_NONE, TP_NONE, true, EVLAN_STRIP_IF_UNTAGGED },
  484. +};
  485. +
  486. static enum dsa_tag_protocol mxl862xx_get_tag_protocol(struct dsa_switch *ds,
  487. int port,
  488. enum dsa_tag_protocol m)
  489. @@ -275,6 +357,7 @@ static int mxl862xx_set_bridge_port(stru
  490. struct mxl862xx_port *p = &priv->ports[port];
  491. u16 bridge_id = dp->bridge ?
  492. priv->bridges[dp->bridge->num] : p->fid;
  493. + u16 vf_scan;
  494. bool enable;
  495. int i, idx;
  496. @@ -283,9 +366,69 @@ static int mxl862xx_set_bridge_port(stru
  497. br_port_cfg.mask = cpu_to_le32(MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_ID |
  498. MXL862XX_BRIDGE_PORT_CONFIG_MASK_BRIDGE_PORT_MAP |
  499. MXL862XX_BRIDGE_PORT_CONFIG_MASK_MC_SRC_MAC_LEARNING |
  500. - MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_SUB_METER);
  501. + MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_SUB_METER |
  502. + MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN |
  503. + MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN |
  504. + MXL862XX_BRIDGE_PORT_CONFIG_MASK_INGRESS_VLAN_FILTER |
  505. + MXL862XX_BRIDGE_PORT_CONFIG_MASK_EGRESS_VLAN_FILTER1 |
  506. + MXL862XX_BRIDGE_PORT_CONFIG_MASK_VLAN_BASED_MAC_LEARNING);
  507. br_port_cfg.src_mac_learning_disable = !p->learning;
  508. + /* Extended VLAN block assignments.
  509. + * Ingress: block_size is sent as-is (all entries are finals).
  510. + * Egress: n_active narrows the scan window to only the
  511. + * entries actually written by evlan_program_egress.
  512. + */
  513. + br_port_cfg.ingress_extended_vlan_enable = p->ingress_evlan.in_use;
  514. + br_port_cfg.ingress_extended_vlan_block_id =
  515. + cpu_to_le16(p->ingress_evlan.block_id);
  516. + br_port_cfg.ingress_extended_vlan_block_size =
  517. + cpu_to_le16(p->ingress_evlan.block_size);
  518. + br_port_cfg.egress_extended_vlan_enable = p->egress_evlan.in_use;
  519. + br_port_cfg.egress_extended_vlan_block_id =
  520. + cpu_to_le16(p->egress_evlan.block_id);
  521. + br_port_cfg.egress_extended_vlan_block_size =
  522. + cpu_to_le16(p->egress_evlan.n_active);
  523. +
  524. + /* VLAN Filter block assignments (per-port).
  525. + * The block_size sent to the firmware narrows the HW scan
  526. + * window to [block_id, block_id + active_count), relying on
  527. + * discard_unmatched_tagged for frames outside that range.
  528. + * When active_count=0, send 1 to scan only the DISCARD
  529. + * sentinel at index 0 (block_size=0 would disable narrowing
  530. + * and scan the entire allocated block).
  531. + *
  532. + * The bridge check ensures VF is disabled when the port
  533. + * leaves the bridge, without needing to prematurely clear
  534. + * vlan_filtering (which the DSA framework handles later via
  535. + * port_vlan_filtering).
  536. + */
  537. + if (p->vf.allocated && p->vlan_filtering &&
  538. + dsa_port_bridge_dev_get(dp)) {
  539. + vf_scan = max_t(u16, p->vf.active_count, 1);
  540. + br_port_cfg.ingress_vlan_filter_enable = 1;
  541. + br_port_cfg.ingress_vlan_filter_block_id =
  542. + cpu_to_le16(p->vf.block_id);
  543. + br_port_cfg.ingress_vlan_filter_block_size =
  544. + cpu_to_le16(vf_scan);
  545. +
  546. + br_port_cfg.egress_vlan_filter1enable = 1;
  547. + br_port_cfg.egress_vlan_filter1block_id =
  548. + cpu_to_le16(p->vf.block_id);
  549. + br_port_cfg.egress_vlan_filter1block_size =
  550. + cpu_to_le16(vf_scan);
  551. + } else {
  552. + br_port_cfg.ingress_vlan_filter_enable = 0;
  553. + br_port_cfg.egress_vlan_filter1enable = 0;
  554. + }
  555. +
  556. + /* IVL when VLAN-aware: include VID in FDB lookup keys so that
  557. + * learned entries are per-VID. In VLAN-unaware mode, SVL is
  558. + * used (VID excluded from key).
  559. + */
  560. + br_port_cfg.vlan_src_mac_vid_enable = p->vlan_filtering;
  561. + br_port_cfg.vlan_dst_mac_vid_enable = p->vlan_filtering;
  562. +
  563. mxl862xx_fw_portmap_from_bitmap(br_port_cfg.bridge_port_map, p->portmap);
  564. for (i = 0; i < ARRAY_SIZE(mxl862xx_flood_meters); i++) {
  565. @@ -329,6 +472,91 @@ static int mxl862xx_sync_bridge_members(
  566. return ret;
  567. }
  568. +static void mxl862xx_evlan_block_init(struct mxl862xx_evlan_block *blk,
  569. + u16 size)
  570. +{
  571. + blk->allocated = false;
  572. + blk->in_use = false;
  573. + blk->block_id = 0;
  574. + blk->block_size = size;
  575. + blk->n_active = 0;
  576. +}
  577. +
  578. +static int mxl862xx_evlan_block_alloc(struct mxl862xx_priv *priv,
  579. + struct mxl862xx_evlan_block *blk)
  580. +{
  581. + struct mxl862xx_extendedvlan_alloc param = {};
  582. + int ret;
  583. +
  584. + param.number_of_entries = cpu_to_le16(blk->block_size);
  585. +
  586. + ret = MXL862XX_API_READ(priv, MXL862XX_EXTENDEDVLAN_ALLOC, param);
  587. + if (ret)
  588. + return ret;
  589. +
  590. + blk->block_id = le16_to_cpu(param.extended_vlan_block_id);
  591. + blk->allocated = true;
  592. +
  593. + return 0;
  594. +}
  595. +
  596. +static void mxl862xx_vf_init(struct mxl862xx_vf_block *vf, u16 size)
  597. +{
  598. + vf->allocated = false;
  599. + vf->block_id = 0;
  600. + vf->block_size = size;
  601. + vf->active_count = 0;
  602. + INIT_LIST_HEAD(&vf->vids);
  603. +}
  604. +
  605. +static int mxl862xx_vf_block_alloc(struct mxl862xx_priv *priv,
  606. + u16 size, u16 *block_id)
  607. +{
  608. + struct mxl862xx_vlanfilter_alloc param = {};
  609. + int ret;
  610. +
  611. + param.number_of_entries = cpu_to_le16(size);
  612. + param.discard_untagged = 0;
  613. + param.discard_unmatched_tagged = 1;
  614. +
  615. + ret = MXL862XX_API_READ(priv, MXL862XX_VLANFILTER_ALLOC, param);
  616. + if (ret)
  617. + return ret;
  618. +
  619. + *block_id = le16_to_cpu(param.vlan_filter_block_id);
  620. + return 0;
  621. +}
  622. +
  623. +static int mxl862xx_vf_entry_discard(struct mxl862xx_priv *priv,
  624. + u16 block_id, u16 index)
  625. +{
  626. + struct mxl862xx_vlanfilter_config cfg = {};
  627. +
  628. + cfg.vlan_filter_block_id = cpu_to_le16(block_id);
  629. + cfg.entry_index = cpu_to_le16(index);
  630. + cfg.vlan_filter_mask = cpu_to_le32(MXL862XX_VLAN_FILTER_TCI_MASK_VID);
  631. + cfg.val = cpu_to_le32(0);
  632. + cfg.discard_matched = 1;
  633. +
  634. + return MXL862XX_API_WRITE(priv, MXL862XX_VLANFILTER_SET, cfg);
  635. +}
  636. +
  637. +static int mxl862xx_vf_alloc(struct mxl862xx_priv *priv,
  638. + struct mxl862xx_vf_block *vf)
  639. +{
  640. + int ret;
  641. +
  642. + ret = mxl862xx_vf_block_alloc(priv, vf->block_size, &vf->block_id);
  643. + if (ret)
  644. + return ret;
  645. +
  646. + vf->allocated = true;
  647. + vf->active_count = 0;
  648. +
  649. + /* Sentinel: block VID-0 when scan window covers only index 0 */
  650. + return mxl862xx_vf_entry_discard(priv, vf->block_id, 0);
  651. +}
  652. +
  653. static int mxl862xx_allocate_bridge(struct mxl862xx_priv *priv, u16 *bridge_id)
  654. {
  655. struct mxl862xx_bridge_alloc br_alloc = {};
  656. @@ -392,6 +620,9 @@ static int mxl862xx_add_single_port_brid
  657. static int mxl862xx_setup(struct dsa_switch *ds)
  658. {
  659. struct mxl862xx_priv *priv = ds->priv;
  660. + int n_user_ports = 0, max_vlans;
  661. + int ingress_finals, vid_rules;
  662. + struct dsa_port *dp;
  663. int ret;
  664. ret = mxl862xx_reset(priv);
  665. @@ -402,6 +633,50 @@ static int mxl862xx_setup(struct dsa_swi
  666. if (ret)
  667. return ret;
  668. + /* Calculate Extended VLAN block sizes.
  669. + * With VLAN Filter handling VID membership checks:
  670. + * Ingress: only final catchall rules (PVID insertion, 802.1Q
  671. + * accept, non-8021Q TPID handling, discard).
  672. + * Block sized to exactly fit the finals -- no per-VID
  673. + * ingress EVLAN rules are needed. (7 entries.)
  674. + * Egress: 2 rules per VID that needs tag stripping (untagged VIDs).
  675. + * No egress final catchalls -- VLAN Filter does the discard.
  676. + * CPU: EVLAN is left disabled on CPU ports -- frames pass
  677. + * through without EVLAN processing.
  678. + *
  679. + * Total EVLAN budget:
  680. + * n_user_ports * (ingress + egress) ≤ 1024.
  681. + * Ingress blocks are small (7 entries), so almost all capacity
  682. + * goes to egress VID rules.
  683. + */
  684. + dsa_switch_for_each_user_port(dp, ds)
  685. + n_user_ports++;
  686. +
  687. + if (n_user_ports) {
  688. + ingress_finals = ARRAY_SIZE(ingress_aware_final);
  689. + vid_rules = ARRAY_SIZE(vid_accept_standard);
  690. +
  691. + /* Ingress block: fixed at finals count (7 entries) */
  692. + priv->evlan_ingress_size = ingress_finals;
  693. +
  694. + /* Egress block: remaining budget divided equally among
  695. + * user ports. Each untagged VID needs vid_rules (2)
  696. + * EVLAN entries for tag stripping. Tagged-only VIDs
  697. + * need no EVLAN rules at all.
  698. + */
  699. + max_vlans = (MXL862XX_TOTAL_EVLAN_ENTRIES -
  700. + n_user_ports * ingress_finals) /
  701. + (n_user_ports * vid_rules);
  702. + priv->evlan_egress_size = vid_rules * max_vlans;
  703. +
  704. + /* VLAN Filter block: one per user port. The 1024-entry
  705. + * table is divided equally among user ports. Each port
  706. + * gets its own VF block for per-port VID membership --
  707. + * discard_unmatched_tagged handles the rest.
  708. + */
  709. + priv->vf_block_size = MXL862XX_TOTAL_VF_ENTRIES / n_user_ports;
  710. + }
  711. +
  712. ret = mxl862xx_setup_drop_meter(ds);
  713. if (ret)
  714. return ret;
  715. @@ -483,27 +758,616 @@ static int mxl862xx_configure_sp_tag_pro
  716. return MXL862XX_API_WRITE(ds->priv, MXL862XX_SS_SPTAG_SET, tag);
  717. }
  718. +/**
  719. + * mxl862xx_evlan_write_rule - Write a single Extended VLAN rule to hardware
  720. + * @priv: driver private data
  721. + * @block_id: HW Extended VLAN block ID
  722. + * @entry_index: entry index within the block
  723. + * @desc: rule descriptor (filter type + action)
  724. + * @vid: VLAN ID for VID-specific rules (ignored when !desc->match_vid)
  725. + * @untagged: strip tag on egress for EVLAN_STRIP_IF_UNTAGGED action
  726. + * @pvid: port VLAN ID for PVID insertion rules (0 = no PVID)
  727. + *
  728. + * Translates a compact rule descriptor into a full firmware
  729. + * mxl862xx_extendedvlan_config struct and writes it via the API.
  730. + */
  731. +static int mxl862xx_evlan_write_rule(struct mxl862xx_priv *priv,
  732. + u16 block_id, u16 entry_index,
  733. + const struct mxl862xx_evlan_rule_desc *desc,
  734. + u16 vid, bool untagged, u16 pvid)
  735. +{
  736. + struct mxl862xx_extendedvlan_config cfg = {};
  737. + struct mxl862xx_extendedvlan_filter_vlan *fv;
  738. +
  739. + cfg.extended_vlan_block_id = cpu_to_le16(block_id);
  740. + cfg.entry_index = cpu_to_le16(entry_index);
  741. +
  742. + /* Populate filter */
  743. + cfg.filter.outer_vlan.type = cpu_to_le32(desc->outer_type);
  744. + cfg.filter.inner_vlan.type = cpu_to_le32(desc->inner_type);
  745. + cfg.filter.outer_vlan.tpid = cpu_to_le32(desc->outer_tpid);
  746. + cfg.filter.inner_vlan.tpid = cpu_to_le32(desc->inner_tpid);
  747. +
  748. + if (desc->match_vid) {
  749. + /* For egress unaware: outer=NO_FILTER, match on inner tag */
  750. + if (desc->outer_type == FT_NO_FILTER)
  751. + fv = &cfg.filter.inner_vlan;
  752. + else
  753. + fv = &cfg.filter.outer_vlan;
  754. +
  755. + fv->vid_enable = 1;
  756. + fv->vid_val = cpu_to_le32(vid);
  757. + }
  758. +
  759. + /* Populate treatment based on action */
  760. + switch (desc->action) {
  761. + case EVLAN_ACCEPT:
  762. + cfg.treatment.remove_tag =
  763. + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG);
  764. + break;
  765. +
  766. + case EVLAN_STRIP_IF_UNTAGGED:
  767. + cfg.treatment.remove_tag = cpu_to_le32(untagged ?
  768. + MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG :
  769. + MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG);
  770. + break;
  771. +
  772. + case EVLAN_DISCARD:
  773. + cfg.treatment.remove_tag =
  774. + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM);
  775. + break;
  776. +
  777. + case EVLAN_PVID_OR_DISCARD:
  778. + if (pvid) {
  779. + cfg.treatment.remove_tag =
  780. + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG);
  781. + cfg.treatment.add_outer_vlan = 1;
  782. + cfg.treatment.outer_vlan.vid_mode =
  783. + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL);
  784. + cfg.treatment.outer_vlan.vid_val = cpu_to_le32(pvid);
  785. + cfg.treatment.outer_vlan.tpid =
  786. + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q);
  787. + } else {
  788. + cfg.treatment.remove_tag =
  789. + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM);
  790. + }
  791. + break;
  792. +
  793. + case EVLAN_PVID_OR_PASS:
  794. + if (pvid) {
  795. + cfg.treatment.remove_tag =
  796. + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG);
  797. + cfg.treatment.add_outer_vlan = 1;
  798. + cfg.treatment.outer_vlan.vid_mode =
  799. + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL);
  800. + cfg.treatment.outer_vlan.vid_val = cpu_to_le32(pvid);
  801. + cfg.treatment.outer_vlan.tpid =
  802. + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q);
  803. + } else {
  804. + cfg.treatment.remove_tag =
  805. + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_NOT_REMOVE_TAG);
  806. + }
  807. + break;
  808. +
  809. + case EVLAN_STRIP1_AND_PVID_OR_DISCARD:
  810. + if (pvid) {
  811. + cfg.treatment.remove_tag =
  812. + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_REMOVE_1_TAG);
  813. + cfg.treatment.add_outer_vlan = 1;
  814. + cfg.treatment.outer_vlan.vid_mode =
  815. + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_VID_VAL);
  816. + cfg.treatment.outer_vlan.vid_val = cpu_to_le32(pvid);
  817. + cfg.treatment.outer_vlan.tpid =
  818. + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_8021Q);
  819. + } else {
  820. + cfg.treatment.remove_tag =
  821. + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM);
  822. + }
  823. + break;
  824. + }
  825. +
  826. + return MXL862XX_API_WRITE(priv, MXL862XX_EXTENDEDVLAN_SET, cfg);
  827. +}
  828. +
  829. +/**
  830. + * mxl862xx_evlan_deactivate_entry - Reset an Extended VLAN entry to no-op
  831. + * @priv: driver private data
  832. + * @block_id: HW Extended VLAN block ID
  833. + * @entry_index: entry index within the block
  834. + *
  835. + * Writes a zeroed-out config to the firmware, which deactivates the
  836. + * rule (making it transparent / no-op).
  837. + */
  838. +static int mxl862xx_evlan_deactivate_entry(struct mxl862xx_priv *priv,
  839. + u16 block_id, u16 entry_index)
  840. +{
  841. + struct mxl862xx_extendedvlan_config cfg = {};
  842. +
  843. + cfg.extended_vlan_block_id = cpu_to_le16(block_id);
  844. + cfg.entry_index = cpu_to_le16(entry_index);
  845. +
  846. + /* Use an unreachable filter (DEFAULT+DEFAULT) with DISCARD treatment.
  847. + * A zeroed entry would have NORMAL+NORMAL filter which matches
  848. + * real double-tagged traffic and passes it through.
  849. + */
  850. + cfg.filter.outer_vlan.type =
  851. + cpu_to_le32(MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT);
  852. + cfg.filter.inner_vlan.type =
  853. + cpu_to_le32(MXL862XX_EXTENDEDVLAN_FILTER_TYPE_DEFAULT);
  854. + cfg.treatment.remove_tag =
  855. + cpu_to_le32(MXL862XX_EXTENDEDVLAN_TREATMENT_DISCARD_UPSTREAM);
  856. +
  857. + return MXL862XX_API_WRITE(priv, MXL862XX_EXTENDEDVLAN_SET, cfg);
  858. +}
  859. +
  860. +/**
  861. + * mxl862xx_evlan_write_final_rules - Write catchall rules to the ingress block
  862. + * @priv: driver private data
  863. + * @blk: Extended VLAN block (already allocated)
  864. + * @rules: array of rule descriptors for the final rules
  865. + * @n_rules: number of final rules
  866. + * @pvid: port VLAN ID (for PVID insertion rules)
  867. + *
  868. + * Writes final catchall rules starting at block_size - n_rules. With
  869. + * VLAN Filter handling VID membership, only the ingress block uses
  870. + * finals, and the block is sized to exactly fit them (no VID entries),
  871. + * so the rules fill the entire block.
  872. + */
  873. +static int mxl862xx_evlan_write_final_rules(struct mxl862xx_priv *priv,
  874. + struct mxl862xx_evlan_block *blk,
  875. + const struct mxl862xx_evlan_rule_desc *rules,
  876. + int n_rules, u16 pvid)
  877. +{
  878. + u16 start_idx = blk->block_size - n_rules;
  879. + int i, ret;
  880. +
  881. + for (i = 0; i < n_rules; i++) {
  882. + ret = mxl862xx_evlan_write_rule(priv, blk->block_id,
  883. + start_idx + i, &rules[i],
  884. + 0, false, pvid);
  885. + if (ret)
  886. + return ret;
  887. + }
  888. +
  889. + return 0;
  890. +}
  891. +
  892. +/**
  893. + * mxl862xx_vf_entry_set - Write a single VLAN Filter entry
  894. + * @priv: driver private data
  895. + * @block_id: HW VLAN Filter block ID
  896. + * @index: entry index within the block
  897. + * @vid: VLAN ID to allow
  898. + *
  899. + * Writes an ALLOW entry (discard_matched=false) for the given VID.
  900. + */
  901. +static int mxl862xx_vf_entry_set(struct mxl862xx_priv *priv,
  902. + u16 block_id, u16 index, u16 vid)
  903. +{
  904. + struct mxl862xx_vlanfilter_config cfg = {};
  905. +
  906. + cfg.vlan_filter_block_id = cpu_to_le16(block_id);
  907. + cfg.entry_index = cpu_to_le16(index);
  908. + cfg.vlan_filter_mask = cpu_to_le32(MXL862XX_VLAN_FILTER_TCI_MASK_VID);
  909. + cfg.val = cpu_to_le32(vid);
  910. + cfg.discard_matched = 0;
  911. +
  912. + return MXL862XX_API_WRITE(priv, MXL862XX_VLANFILTER_SET, cfg);
  913. +}
  914. +
  915. +/**
  916. + * mxl862xx_vf_find_vid - Find a VID entry in a VF block
  917. + * @vf: VLAN Filter block to search
  918. + * @vid: VLAN ID to find
  919. + */
  920. +static struct mxl862xx_vf_vid *
  921. +mxl862xx_vf_find_vid(struct mxl862xx_vf_block *vf, u16 vid)
  922. +{
  923. + struct mxl862xx_vf_vid *ve;
  924. +
  925. + list_for_each_entry(ve, &vf->vids, list)
  926. + if (ve->vid == vid)
  927. + return ve;
  928. +
  929. + return NULL;
  930. +}
  931. +
  932. +/**
  933. + * mxl862xx_vf_add_vid - Add a VID to a port's VLAN Filter block
  934. + * @priv: driver private data
  935. + * @vf: VLAN Filter block
  936. + * @vid: VLAN ID to add
  937. + * @untagged: whether this VID should strip tags on egress
  938. + *
  939. + * Idempotent. Writes an ALLOW entry at active_count and increments
  940. + * active_count. If the VID already exists, only the untagged flag
  941. + * is updated. The HW block must be allocated before calling this.
  942. + */
  943. +static int mxl862xx_vf_add_vid(struct mxl862xx_priv *priv,
  944. + struct mxl862xx_vf_block *vf,
  945. + u16 vid, bool untagged)
  946. +{
  947. + struct mxl862xx_vf_vid *ve;
  948. + int ret;
  949. +
  950. + ve = mxl862xx_vf_find_vid(vf, vid);
  951. + if (ve) {
  952. + ve->untagged = untagged;
  953. + return 0;
  954. + }
  955. +
  956. + if (vf->active_count >= vf->block_size)
  957. + return -ENOSPC;
  958. +
  959. + ve = kzalloc(sizeof(*ve), GFP_KERNEL);
  960. + if (!ve)
  961. + return -ENOMEM;
  962. +
  963. + ve->vid = vid;
  964. + ve->index = vf->active_count;
  965. + ve->untagged = untagged;
  966. +
  967. + ret = mxl862xx_vf_entry_set(priv, vf->block_id, ve->index, vid);
  968. + if (ret) {
  969. + kfree(ve);
  970. + return ret;
  971. + }
  972. +
  973. + list_add_tail(&ve->list, &vf->vids);
  974. + vf->active_count++;
  975. +
  976. + return 0;
  977. +}
  978. +
  979. +/**
  980. + * mxl862xx_vf_del_vid - Remove a VID from a port's VLAN Filter block
  981. + * @priv: driver private data
  982. + * @vf: VLAN Filter block
  983. + * @vid: VLAN ID to remove
  984. + *
  985. + * Swap-compacts: the last active entry is moved into the gap,
  986. + * active_count is decremented, and the old last slot is plugged
  987. + * with DISCARD. When active_count drops to 0, a DISCARD sentinel
  988. + * is restored at index 0.
  989. + */
  990. +static int mxl862xx_vf_del_vid(struct mxl862xx_priv *priv,
  991. + struct mxl862xx_vf_block *vf, u16 vid)
  992. +{
  993. + struct mxl862xx_vf_vid *ve, *last_ve;
  994. + u16 gap, last;
  995. + int ret;
  996. +
  997. + ve = mxl862xx_vf_find_vid(vf, vid);
  998. + if (!ve)
  999. + return 0;
  1000. +
  1001. + if (!vf->allocated) {
  1002. + /* Software-only state -- just remove the tracking entry */
  1003. + list_del(&ve->list);
  1004. + kfree(ve);
  1005. + vf->active_count--;
  1006. + return 0;
  1007. + }
  1008. +
  1009. + gap = ve->index;
  1010. + last = vf->active_count - 1;
  1011. +
  1012. + if (vf->active_count == 1) {
  1013. + /* Last VID -- restore DISCARD sentinel at index 0 */
  1014. + ret = mxl862xx_vf_entry_discard(priv, vf->block_id, 0);
  1015. + if (ret)
  1016. + return ret;
  1017. + } else if (gap < last) {
  1018. + /* Swap: move the last ALLOW entry into the gap */
  1019. + last_ve = NULL;
  1020. + list_for_each_entry(last_ve, &vf->vids, list)
  1021. + if (last_ve->index == last)
  1022. + break;
  1023. +
  1024. + if (WARN_ON(!last_ve || last_ve->index != last))
  1025. + return -EINVAL;
  1026. +
  1027. + ret = mxl862xx_vf_entry_set(priv, vf->block_id,
  1028. + gap, last_ve->vid);
  1029. + if (ret)
  1030. + return ret;
  1031. +
  1032. + last_ve->index = gap;
  1033. +
  1034. + /* Plug the old last slot with DISCARD */
  1035. + ret = mxl862xx_vf_entry_discard(priv, vf->block_id, last);
  1036. + if (ret)
  1037. + return ret;
  1038. + } else {
  1039. + /* Deleting the last entry -- just plug it */
  1040. + ret = mxl862xx_vf_entry_discard(priv, vf->block_id, last);
  1041. + if (ret)
  1042. + return ret;
  1043. + }
  1044. +
  1045. + list_del(&ve->list);
  1046. + kfree(ve);
  1047. + vf->active_count--;
  1048. +
  1049. + return 0;
  1050. +}
  1051. +
  1052. +/**
  1053. + * mxl862xx_evlan_program_ingress - Write the fixed ingress catchall rules
  1054. + * @priv: driver private data
  1055. + * @port: port number
  1056. + *
  1057. + * In VLAN-aware mode the ingress EVLAN block handles PVID insertion for
  1058. + * untagged/priority-tagged frames, passes through standard 802.1Q
  1059. + * tagged frames for VF membership checking, and treats non-8021Q TPID
  1060. + * frames as untagged. The block is sized to exactly fit the 7 catchall
  1061. + * rules and is rewritten whenever PVID changes.
  1062. + *
  1063. + * In VLAN-unaware mode the firmware passes frames through unchanged when
  1064. + * no ingress block is assigned, so nothing is programmed.
  1065. + */
  1066. +static int mxl862xx_evlan_program_ingress(struct mxl862xx_priv *priv, int port)
  1067. +{
  1068. + struct mxl862xx_port *p = &priv->ports[port];
  1069. + struct mxl862xx_evlan_block *blk = &p->ingress_evlan;
  1070. +
  1071. + if (!p->vlan_filtering)
  1072. + return 0;
  1073. +
  1074. + blk->in_use = true;
  1075. + blk->n_active = blk->block_size;
  1076. +
  1077. + return mxl862xx_evlan_write_final_rules(priv, blk,
  1078. + ingress_aware_final,
  1079. + ARRAY_SIZE(ingress_aware_final),
  1080. + p->pvid);
  1081. +}
  1082. +
  1083. +/**
  1084. + * mxl862xx_evlan_program_egress - Reprogram all egress tag-stripping rules
  1085. + * @priv: driver private data
  1086. + * @port: port number
  1087. + *
  1088. + * Walks the port's VF VID list and writes 2 EVLAN rules per VID that
  1089. + * needs egress tag stripping. In VLAN-aware mode only untagged VIDs
  1090. + * need rules (tagged VIDs pass through EVLAN untouched). In unaware
  1091. + * mode every VID gets rules.
  1092. + *
  1093. + * Entries are packed starting at index 0, and the scan window
  1094. + * (n_active) is narrowed so stale entries beyond it are never matched.
  1095. + */
  1096. +static int mxl862xx_evlan_program_egress(struct mxl862xx_priv *priv, int port)
  1097. +{
  1098. + struct mxl862xx_port *p = &priv->ports[port];
  1099. + struct mxl862xx_evlan_block *blk = &p->egress_evlan;
  1100. + const struct mxl862xx_evlan_rule_desc *vid_rules;
  1101. + struct mxl862xx_vf_vid *vfv;
  1102. + u16 old_active = blk->n_active;
  1103. + u16 idx = 0, i;
  1104. + int n_vid, ret;
  1105. +
  1106. + if (p->vlan_filtering) {
  1107. + vid_rules = vid_accept_standard;
  1108. + n_vid = ARRAY_SIZE(vid_accept_standard);
  1109. + } else {
  1110. + vid_rules = vid_accept_egress_unaware;
  1111. + n_vid = ARRAY_SIZE(vid_accept_egress_unaware);
  1112. + }
  1113. +
  1114. + list_for_each_entry(vfv, &p->vf.vids, list) {
  1115. + /* In VLAN-aware mode tagged-only VIDs need no EVLAN
  1116. + * rules -- VLAN Filter handles membership.
  1117. + */
  1118. + if (p->vlan_filtering && !vfv->untagged)
  1119. + continue;
  1120. +
  1121. + if (idx + n_vid > blk->block_size)
  1122. + return -ENOSPC;
  1123. +
  1124. + ret = mxl862xx_evlan_write_rule(priv, blk->block_id,
  1125. + idx++, &vid_rules[0],
  1126. + vfv->vid, vfv->untagged,
  1127. + p->pvid);
  1128. + if (ret)
  1129. + return ret;
  1130. +
  1131. + if (n_vid > 1) {
  1132. + ret = mxl862xx_evlan_write_rule(priv, blk->block_id,
  1133. + idx++, &vid_rules[1],
  1134. + vfv->vid,
  1135. + vfv->untagged,
  1136. + p->pvid);
  1137. + if (ret)
  1138. + return ret;
  1139. + }
  1140. + }
  1141. +
  1142. + /* Deactivate stale entries that are no longer needed.
  1143. + * This closes the brief window between writing the new rules
  1144. + * and set_bridge_port narrowing the scan window.
  1145. + */
  1146. + for (i = idx; i < old_active; i++) {
  1147. + ret = mxl862xx_evlan_deactivate_entry(priv,
  1148. + blk->block_id,
  1149. + i);
  1150. + if (ret)
  1151. + return ret;
  1152. + }
  1153. +
  1154. + blk->n_active = idx;
  1155. + blk->in_use = idx > 0;
  1156. +
  1157. + return 0;
  1158. +}
  1159. +
  1160. +static int mxl862xx_port_vlan_filtering(struct dsa_switch *ds, int port,
  1161. + bool vlan_filtering,
  1162. + struct netlink_ext_ack *extack)
  1163. +{
  1164. + struct mxl862xx_priv *priv = ds->priv;
  1165. + struct mxl862xx_port *p = &priv->ports[port];
  1166. + bool changed = (p->vlan_filtering != vlan_filtering);
  1167. + int ret;
  1168. +
  1169. + p->vlan_filtering = vlan_filtering;
  1170. +
  1171. + /* Reprogram Extended VLAN rules if filtering mode changed */
  1172. + if (changed) {
  1173. + /* When leaving VLAN-aware mode, release the ingress HW
  1174. + * block. The firmware passes frames through unchanged
  1175. + * when no ingress EVLAN block is assigned, so the block
  1176. + * is unnecessary in unaware mode.
  1177. + */
  1178. + if (!vlan_filtering)
  1179. + p->ingress_evlan.in_use = false;
  1180. +
  1181. + ret = mxl862xx_evlan_program_ingress(priv, port);
  1182. + if (ret)
  1183. + return ret;
  1184. +
  1185. + ret = mxl862xx_evlan_program_egress(priv, port);
  1186. + if (ret)
  1187. + return ret;
  1188. + }
  1189. +
  1190. + /* Push VLAN-based MAC learning flags and (possibly newly
  1191. + * allocated) ingress block to hardware.
  1192. + */
  1193. + return mxl862xx_set_bridge_port(ds, port);
  1194. +}
  1195. +
  1196. +static int mxl862xx_port_vlan_add(struct dsa_switch *ds, int port,
  1197. + const struct switchdev_obj_port_vlan *vlan,
  1198. + struct netlink_ext_ack *extack)
  1199. +{
  1200. + struct mxl862xx_priv *priv = ds->priv;
  1201. + struct mxl862xx_port *p = &priv->ports[port];
  1202. + bool untagged = !!(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED);
  1203. + u16 vid = vlan->vid;
  1204. + u16 old_pvid = p->pvid;
  1205. + bool pvid_changed = false;
  1206. + int ret;
  1207. +
  1208. + /* CPU port is VLAN-transparent: the SP tag handles port
  1209. + * identification and the host-side DSA tagger manages VLAN
  1210. + * delivery. Egress EVLAN catchalls are set up once in
  1211. + * setup_cpu_bridge; no per-VID VF/EVLAN programming needed.
  1212. + */
  1213. + if (dsa_is_cpu_port(ds, port))
  1214. + return 0;
  1215. +
  1216. + /* Update PVID tracking */
  1217. + if (vlan->flags & BRIDGE_VLAN_INFO_PVID) {
  1218. + if (p->pvid != vid) {
  1219. + p->pvid = vid;
  1220. + pvid_changed = true;
  1221. + }
  1222. + } else if (p->pvid == vid) {
  1223. + p->pvid = 0;
  1224. + pvid_changed = true;
  1225. + }
  1226. +
  1227. + /* Add/update VID in this port's VLAN Filter block.
  1228. + * VF must be updated before programming egress EVLAN because
  1229. + * evlan_program_egress walks the VF VID list.
  1230. + */
  1231. + ret = mxl862xx_vf_add_vid(priv, &p->vf, vid, untagged);
  1232. + if (ret)
  1233. + goto err_pvid;
  1234. +
  1235. + /* Reprogram ingress finals if PVID changed */
  1236. + if (pvid_changed) {
  1237. + ret = mxl862xx_evlan_program_ingress(priv, port);
  1238. + if (ret)
  1239. + goto err_pvid;
  1240. + }
  1241. +
  1242. + /* Reprogram egress tag-stripping rules (walks VF VID list) */
  1243. + ret = mxl862xx_evlan_program_egress(priv, port);
  1244. + if (ret)
  1245. + goto err_pvid;
  1246. +
  1247. + /* Apply VLAN block IDs and MAC learning flags to bridge port */
  1248. + ret = mxl862xx_set_bridge_port(ds, port);
  1249. + if (ret)
  1250. + goto err_pvid;
  1251. +
  1252. + return 0;
  1253. +
  1254. +err_pvid:
  1255. + p->pvid = old_pvid;
  1256. + return ret;
  1257. +}
  1258. +
  1259. +static int mxl862xx_port_vlan_del(struct dsa_switch *ds, int port,
  1260. + const struct switchdev_obj_port_vlan *vlan)
  1261. +{
  1262. + struct mxl862xx_priv *priv = ds->priv;
  1263. + struct mxl862xx_port *p = &priv->ports[port];
  1264. + u16 vid = vlan->vid;
  1265. + bool pvid_changed = false;
  1266. + int ret;
  1267. +
  1268. + if (dsa_is_cpu_port(ds, port))
  1269. + return 0;
  1270. +
  1271. + /* Clear PVID if we're deleting it */
  1272. + if (p->pvid == vid) {
  1273. + p->pvid = 0;
  1274. + pvid_changed = true;
  1275. + }
  1276. +
  1277. + /* Remove VID from this port's VLAN Filter block.
  1278. + * Must happen before egress reprogram so the VID is no
  1279. + * longer in the list that evlan_program_egress walks.
  1280. + */
  1281. + ret = mxl862xx_vf_del_vid(priv, &p->vf, vid);
  1282. + if (ret)
  1283. + return ret;
  1284. +
  1285. + /* Reprogram egress tag-stripping rules (VID is now gone) */
  1286. + ret = mxl862xx_evlan_program_egress(priv, port);
  1287. + if (ret)
  1288. + return ret;
  1289. +
  1290. + /* If PVID changed, reprogram ingress finals */
  1291. + if (pvid_changed) {
  1292. + ret = mxl862xx_evlan_program_ingress(priv, port);
  1293. + if (ret)
  1294. + return ret;
  1295. + }
  1296. +
  1297. + return mxl862xx_set_bridge_port(ds, port);
  1298. +}
  1299. +
  1300. static int mxl862xx_setup_cpu_bridge(struct dsa_switch *ds, int port)
  1301. {
  1302. struct mxl862xx_priv *priv = ds->priv;
  1303. + struct mxl862xx_port *p = &priv->ports[port];
  1304. struct dsa_port *dp;
  1305. - priv->ports[port].fid = MXL862XX_DEFAULT_BRIDGE;
  1306. - priv->ports[port].learning = true;
  1307. + p->fid = MXL862XX_DEFAULT_BRIDGE;
  1308. + p->learning = true;
  1309. +
  1310. + /* EVLAN is left disabled on CPU ports -- frames pass through
  1311. + * without EVLAN processing. Only the portmap and bridge
  1312. + * assignment need to be configured.
  1313. + */
  1314. /* include all assigned user ports in the CPU portmap */
  1315. - bitmap_zero(priv->ports[port].portmap, MXL862XX_MAX_BRIDGE_PORTS);
  1316. + bitmap_zero(p->portmap, MXL862XX_MAX_BRIDGE_PORTS);
  1317. dsa_switch_for_each_user_port(dp, ds) {
  1318. /* it's safe to rely on cpu_dp being valid for user ports */
  1319. if (dp->cpu_dp->index != port)
  1320. continue;
  1321. - __set_bit(dp->index, priv->ports[port].portmap);
  1322. + __set_bit(dp->index, p->portmap);
  1323. }
  1324. return mxl862xx_set_bridge_port(ds, port);
  1325. }
  1326. +
  1327. static int mxl862xx_port_bridge_join(struct dsa_switch *ds, int port,
  1328. const struct dsa_bridge bridge,
  1329. bool *tx_fwd_offload,
  1330. @@ -553,6 +1417,22 @@ static void mxl862xx_port_bridge_leave(s
  1331. bitmap_zero(p->portmap, MXL862XX_MAX_BRIDGE_PORTS);
  1332. __set_bit(dp->cpu_dp->index, p->portmap);
  1333. p->flood_block = 0;
  1334. +
  1335. + /* Detach EVLAN and VF blocks from the bridge port BEFORE freeing
  1336. + * them. The firmware tracks a usage count per block and rejects
  1337. + * FREE while the count is non-zero.
  1338. + *
  1339. + * For EVLAN: setting in_use=false makes set_bridge_port send
  1340. + * enable=false, which decrements the firmware refcount.
  1341. + *
  1342. + * For VF: set_bridge_port sees dp->bridge == NULL (DSA already
  1343. + * cleared it) and sends vlan_filter_enable=0, which decrements
  1344. + * the firmware VF refcount.
  1345. + */
  1346. + p->pvid = 0;
  1347. + p->ingress_evlan.in_use = false;
  1348. + p->egress_evlan.in_use = false;
  1349. +
  1350. err = mxl862xx_set_bridge_port(ds, port);
  1351. if (err)
  1352. dev_err(ds->dev,
  1353. @@ -602,6 +1482,28 @@ static int mxl862xx_port_setup(struct ds
  1354. if (ret)
  1355. return ret;
  1356. + /* Initialize and pre-allocate per-port EVLAN and VF blocks for
  1357. + * user ports. CPU ports do not use EVLAN or VF -- frames pass
  1358. + * through without processing. Pre-allocation avoids firmware
  1359. + * EVLAN table fragmentation and simplifies control flow.
  1360. + */
  1361. + mxl862xx_evlan_block_init(&priv->ports[port].ingress_evlan,
  1362. + priv->evlan_ingress_size);
  1363. + ret = mxl862xx_evlan_block_alloc(priv, &priv->ports[port].ingress_evlan);
  1364. + if (ret)
  1365. + return ret;
  1366. +
  1367. + mxl862xx_evlan_block_init(&priv->ports[port].egress_evlan,
  1368. + priv->evlan_egress_size);
  1369. + ret = mxl862xx_evlan_block_alloc(priv, &priv->ports[port].egress_evlan);
  1370. + if (ret)
  1371. + return ret;
  1372. +
  1373. + mxl862xx_vf_init(&priv->ports[port].vf, priv->vf_block_size);
  1374. + ret = mxl862xx_vf_alloc(priv, &priv->ports[port].vf);
  1375. + if (ret)
  1376. + return ret;
  1377. +
  1378. priv->ports[port].setup_done = true;
  1379. return 0;
  1380. @@ -1012,6 +1914,9 @@ static const struct dsa_switch_ops mxl86
  1381. .port_fdb_dump = mxl862xx_port_fdb_dump,
  1382. .port_mdb_add = mxl862xx_port_mdb_add,
  1383. .port_mdb_del = mxl862xx_port_mdb_del,
  1384. + .port_vlan_filtering = mxl862xx_port_vlan_filtering,
  1385. + .port_vlan_add = mxl862xx_port_vlan_add,
  1386. + .port_vlan_del = mxl862xx_port_vlan_del,
  1387. };
  1388. static void mxl862xx_phylink_mac_config(struct phylink_config *config,
  1389. --- a/drivers/net/dsa/mxl862xx/mxl862xx.h
  1390. +++ b/drivers/net/dsa/mxl862xx/mxl862xx.h
  1391. @@ -13,6 +13,8 @@ struct mxl862xx_priv;
  1392. #define MXL862XX_DEFAULT_BRIDGE 0
  1393. #define MXL862XX_MAX_BRIDGES 48
  1394. #define MXL862XX_MAX_BRIDGE_PORTS 128
  1395. +#define MXL862XX_TOTAL_EVLAN_ENTRIES 1024
  1396. +#define MXL862XX_TOTAL_VF_ENTRIES 1024
  1397. /* Number of __le16 words in a firmware portmap (128-bit bitmap). */
  1398. #define MXL862XX_FW_PORTMAP_WORDS (MXL862XX_MAX_BRIDGE_PORTS / 16)
  1399. @@ -86,6 +88,66 @@ static inline bool mxl862xx_fw_portmap_i
  1400. }
  1401. /**
  1402. + * struct mxl862xx_vf_vid - Per-VID entry within a VLAN Filter block
  1403. + * @list: Linked into &mxl862xx_vf_block.vids
  1404. + * @vid: VLAN ID
  1405. + * @index: Entry index within the VLAN Filter HW block
  1406. + * @untagged: Strip tag on egress for this VID (drives EVLAN tag-stripping)
  1407. + */
  1408. +struct mxl862xx_vf_vid {
  1409. + struct list_head list;
  1410. + u16 vid;
  1411. + u16 index;
  1412. + bool untagged;
  1413. +};
  1414. +
  1415. +/**
  1416. + * struct mxl862xx_vf_block - Per-port VLAN Filter block
  1417. + * @allocated: Whether the HW block has been allocated via VLANFILTER_ALLOC
  1418. + * @block_id: HW VLAN Filter block ID from VLANFILTER_ALLOC
  1419. + * @block_size: Total entries allocated in this block
  1420. + * @active_count: Number of ALLOW entries at indices [0, active_count).
  1421. + * The bridge port config sends max(active_count, 1) as
  1422. + * block_size to narrow the HW scan window.
  1423. + * discard_unmatched_tagged handles frames outside this range.
  1424. + * @vids: List of &mxl862xx_vf_vid entries programmed in this block
  1425. + */
  1426. +struct mxl862xx_vf_block {
  1427. + bool allocated;
  1428. + u16 block_id;
  1429. + u16 block_size;
  1430. + u16 active_count;
  1431. + struct list_head vids;
  1432. +};
  1433. +
  1434. +/**
  1435. + * struct mxl862xx_evlan_block - Per-port per-direction extended VLAN block
  1436. + * @allocated: Whether the HW block has been allocated via EXTENDEDVLAN_ALLOC.
  1437. + * Guards alloc/free idempotency--the block_id is only valid
  1438. + * while allocated is true.
  1439. + * @in_use: Whether the EVLAN engine should be enabled for this block
  1440. + * on the bridge port (sent as the enable flag in
  1441. + * set_bridge_port). Can be false while allocated is still
  1442. + * true -- e.g. when all egress VIDs are removed (idx == 0 in
  1443. + * evlan_program_egress) the block stays allocated for
  1444. + * potential reuse, but the engine is disabled so an empty
  1445. + * rule set does not discard all traffic.
  1446. + * @block_id: HW block ID from EXTENDEDVLAN_ALLOC
  1447. + * @block_size: Total entries allocated
  1448. + * @n_active: Number of HW entries currently written. The bridge port
  1449. + * config sends this as the egress scan window, so entries
  1450. + * beyond n_active are never scanned. Always equals
  1451. + * block_size for ingress blocks (fixed catchall rules).
  1452. + */
  1453. +struct mxl862xx_evlan_block {
  1454. + bool allocated;
  1455. + bool in_use;
  1456. + u16 block_id;
  1457. + u16 block_size;
  1458. + u16 n_active;
  1459. +};
  1460. +
  1461. +/**
  1462. * struct mxl862xx_port - per-port state tracked by the driver
  1463. * @priv: back-pointer to switch private data; needed by
  1464. * deferred work handlers to access ds and priv
  1465. @@ -101,6 +163,11 @@ static inline bool mxl862xx_fw_portmap_i
  1466. * @setup_done: set at end of port_setup, cleared at start of
  1467. * port_teardown; guards deferred work against
  1468. * acting on torn-down state
  1469. + * @pvid: port VLAN ID (native VLAN) assigned to untagged traffic
  1470. + * @vlan_filtering: true when VLAN filtering is enabled on this port
  1471. + * @vf: per-port VLAN Filter block state
  1472. + * @ingress_evlan: ingress extended VLAN block state
  1473. + * @egress_evlan: egress extended VLAN block state
  1474. * @host_flood_uc: desired host unicast flood state (true = flood);
  1475. * updated atomically by port_set_host_flood, consumed
  1476. * by the deferred host_flood_work
  1477. @@ -119,6 +186,12 @@ struct mxl862xx_port {
  1478. unsigned long flood_block;
  1479. bool learning;
  1480. bool setup_done;
  1481. + /* VLAN state */
  1482. + u16 pvid;
  1483. + bool vlan_filtering;
  1484. + struct mxl862xx_vf_block vf;
  1485. + struct mxl862xx_evlan_block ingress_evlan;
  1486. + struct mxl862xx_evlan_block egress_evlan;
  1487. bool host_flood_uc;
  1488. bool host_flood_mc;
  1489. struct work_struct host_flood_work;
  1490. @@ -126,17 +199,23 @@ struct mxl862xx_port {
  1491. /**
  1492. * struct mxl862xx_priv - driver private data for an MxL862xx switch
  1493. - * @ds: pointer to the DSA switch instance
  1494. - * @mdiodev: MDIO device used to communicate with the switch firmware
  1495. - * @crc_err_work: deferred work for shutting down all ports on MDIO CRC errors
  1496. - * @crc_err: set atomically before CRC-triggered shutdown, cleared after
  1497. - * @drop_meter: index of the single shared zero-rate firmware meter used
  1498. - * to unconditionally drop traffic (used to block flooding)
  1499. - * @ports: per-port state, indexed by switch port number
  1500. - * @bridges: maps DSA bridge number to firmware bridge ID;
  1501. - * zero means no firmware bridge allocated for that
  1502. - * DSA bridge number. Indexed by dsa_bridge.num
  1503. - * (0 .. ds->max_num_bridges).
  1504. + * @ds: pointer to the DSA switch instance
  1505. + * @mdiodev: MDIO device used to communicate with the switch firmware
  1506. + * @crc_err_work: deferred work for shutting down all ports on MDIO CRC
  1507. + * errors
  1508. + * @crc_err: set atomically before CRC-triggered shutdown, cleared
  1509. + * after
  1510. + * @drop_meter: index of the single shared zero-rate firmware meter
  1511. + * used to unconditionally drop traffic (used to block
  1512. + * flooding)
  1513. + * @ports: per-port state, indexed by switch port number
  1514. + * @bridges: maps DSA bridge number to firmware bridge ID;
  1515. + * zero means no firmware bridge allocated for that
  1516. + * DSA bridge number. Indexed by dsa_bridge.num
  1517. + * (0 .. ds->max_num_bridges).
  1518. + * @evlan_ingress_size: per-port ingress Extended VLAN block size
  1519. + * @evlan_egress_size: per-port egress Extended VLAN block size
  1520. + * @vf_block_size: per-port VLAN Filter block size
  1521. */
  1522. struct mxl862xx_priv {
  1523. struct dsa_switch *ds;
  1524. @@ -146,6 +225,9 @@ struct mxl862xx_priv {
  1525. u16 drop_meter;
  1526. struct mxl862xx_port ports[MXL862XX_MAX_PORTS];
  1527. u16 bridges[MXL862XX_MAX_BRIDGES + 1];
  1528. + u16 evlan_ingress_size;
  1529. + u16 evlan_egress_size;
  1530. + u16 vf_block_size;
  1531. };
  1532. #endif /* __MXL862XX_H */