1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | |
3 | #include "lan966x_main.h" |
4 | |
5 | #define VLANACCESS_CMD_IDLE 0 |
6 | #define VLANACCESS_CMD_READ 1 |
7 | #define VLANACCESS_CMD_WRITE 2 |
8 | #define VLANACCESS_CMD_INIT 3 |
9 | |
10 | static int lan966x_vlan_get_status(struct lan966x *lan966x) |
11 | { |
12 | return lan_rd(lan966x, ANA_VLANACCESS); |
13 | } |
14 | |
15 | static int lan966x_vlan_wait_for_completion(struct lan966x *lan966x) |
16 | { |
17 | u32 val; |
18 | |
19 | return readx_poll_timeout(lan966x_vlan_get_status, |
20 | lan966x, val, |
21 | (val & ANA_VLANACCESS_VLAN_TBL_CMD) == |
22 | VLANACCESS_CMD_IDLE, |
23 | TABLE_UPDATE_SLEEP_US, TABLE_UPDATE_TIMEOUT_US); |
24 | } |
25 | |
26 | static void lan966x_vlan_set_mask(struct lan966x *lan966x, u16 vid) |
27 | { |
28 | u16 mask = lan966x->vlan_mask[vid]; |
29 | bool cpu_dis; |
30 | |
31 | cpu_dis = !(mask & BIT(CPU_PORT)); |
32 | |
33 | /* Set flags and the VID to configure */ |
34 | lan_rmw(ANA_VLANTIDX_VLAN_PGID_CPU_DIS_SET(cpu_dis) | |
35 | ANA_VLANTIDX_V_INDEX_SET(vid), |
36 | ANA_VLANTIDX_VLAN_PGID_CPU_DIS | |
37 | ANA_VLANTIDX_V_INDEX, |
38 | lan966x, ANA_VLANTIDX); |
39 | |
40 | /* Set the vlan port members mask */ |
41 | lan_rmw(ANA_VLAN_PORT_MASK_VLAN_PORT_MASK_SET(mask), |
42 | ANA_VLAN_PORT_MASK_VLAN_PORT_MASK, |
43 | lan966x, ANA_VLAN_PORT_MASK); |
44 | |
45 | /* Issue a write command */ |
46 | lan_rmw(ANA_VLANACCESS_VLAN_TBL_CMD_SET(VLANACCESS_CMD_WRITE), |
47 | ANA_VLANACCESS_VLAN_TBL_CMD, |
48 | lan966x, ANA_VLANACCESS); |
49 | |
50 | if (lan966x_vlan_wait_for_completion(lan966x)) |
51 | dev_err(lan966x->dev, "Vlan set mask failed\n" ); |
52 | } |
53 | |
54 | static void lan966x_vlan_port_add_vlan_mask(struct lan966x_port *port, u16 vid) |
55 | { |
56 | struct lan966x *lan966x = port->lan966x; |
57 | u8 p = port->chip_port; |
58 | |
59 | lan966x->vlan_mask[vid] |= BIT(p); |
60 | lan966x_vlan_set_mask(lan966x, vid); |
61 | } |
62 | |
63 | static void lan966x_vlan_port_del_vlan_mask(struct lan966x_port *port, u16 vid) |
64 | { |
65 | struct lan966x *lan966x = port->lan966x; |
66 | u8 p = port->chip_port; |
67 | |
68 | lan966x->vlan_mask[vid] &= ~BIT(p); |
69 | lan966x_vlan_set_mask(lan966x, vid); |
70 | } |
71 | |
72 | static bool lan966x_vlan_port_any_vlan_mask(struct lan966x *lan966x, u16 vid) |
73 | { |
74 | return !!(lan966x->vlan_mask[vid] & ~BIT(CPU_PORT)); |
75 | } |
76 | |
77 | static void lan966x_vlan_cpu_add_vlan_mask(struct lan966x *lan966x, u16 vid) |
78 | { |
79 | lan966x->vlan_mask[vid] |= BIT(CPU_PORT); |
80 | lan966x_vlan_set_mask(lan966x, vid); |
81 | } |
82 | |
83 | static void lan966x_vlan_cpu_del_vlan_mask(struct lan966x *lan966x, u16 vid) |
84 | { |
85 | lan966x->vlan_mask[vid] &= ~BIT(CPU_PORT); |
86 | lan966x_vlan_set_mask(lan966x, vid); |
87 | } |
88 | |
89 | static void lan966x_vlan_cpu_add_cpu_vlan_mask(struct lan966x *lan966x, u16 vid) |
90 | { |
91 | __set_bit(vid, lan966x->cpu_vlan_mask); |
92 | } |
93 | |
94 | static void lan966x_vlan_cpu_del_cpu_vlan_mask(struct lan966x *lan966x, u16 vid) |
95 | { |
96 | __clear_bit(vid, lan966x->cpu_vlan_mask); |
97 | } |
98 | |
99 | bool lan966x_vlan_cpu_member_cpu_vlan_mask(struct lan966x *lan966x, u16 vid) |
100 | { |
101 | return test_bit(vid, lan966x->cpu_vlan_mask); |
102 | } |
103 | |
104 | static u16 lan966x_vlan_port_get_pvid(struct lan966x_port *port) |
105 | { |
106 | struct lan966x *lan966x = port->lan966x; |
107 | |
108 | if (!(lan966x->bridge_mask & BIT(port->chip_port))) |
109 | return HOST_PVID; |
110 | |
111 | return port->vlan_aware ? port->pvid : UNAWARE_PVID; |
112 | } |
113 | |
114 | int lan966x_vlan_port_set_vid(struct lan966x_port *port, u16 vid, |
115 | bool pvid, bool untagged) |
116 | { |
117 | struct lan966x *lan966x = port->lan966x; |
118 | |
119 | /* Egress vlan classification */ |
120 | if (untagged && port->vid != vid) { |
121 | if (port->vid) { |
122 | dev_err(lan966x->dev, |
123 | "Port already has a native VLAN: %d\n" , |
124 | port->vid); |
125 | return -EBUSY; |
126 | } |
127 | port->vid = vid; |
128 | } |
129 | |
130 | /* Default ingress vlan classification */ |
131 | if (pvid) |
132 | port->pvid = vid; |
133 | |
134 | return 0; |
135 | } |
136 | |
137 | static void lan966x_vlan_port_remove_vid(struct lan966x_port *port, u16 vid) |
138 | { |
139 | if (port->pvid == vid) |
140 | port->pvid = 0; |
141 | |
142 | if (port->vid == vid) |
143 | port->vid = 0; |
144 | } |
145 | |
146 | void lan966x_vlan_port_set_vlan_aware(struct lan966x_port *port, |
147 | bool vlan_aware) |
148 | { |
149 | port->vlan_aware = vlan_aware; |
150 | } |
151 | |
152 | void lan966x_vlan_port_apply(struct lan966x_port *port) |
153 | { |
154 | struct lan966x *lan966x = port->lan966x; |
155 | u16 pvid; |
156 | u32 val; |
157 | |
158 | pvid = lan966x_vlan_port_get_pvid(port); |
159 | |
160 | /* Ingress clasification (ANA_PORT_VLAN_CFG) */ |
161 | /* Default vlan to classify for untagged frames (may be zero) */ |
162 | val = ANA_VLAN_CFG_VLAN_VID_SET(pvid); |
163 | if (port->vlan_aware) |
164 | val |= ANA_VLAN_CFG_VLAN_AWARE_ENA_SET(1) | |
165 | ANA_VLAN_CFG_VLAN_POP_CNT_SET(1); |
166 | |
167 | lan_rmw(val, |
168 | ANA_VLAN_CFG_VLAN_VID | ANA_VLAN_CFG_VLAN_AWARE_ENA | |
169 | ANA_VLAN_CFG_VLAN_POP_CNT, |
170 | lan966x, ANA_VLAN_CFG(port->chip_port)); |
171 | |
172 | lan_rmw(DEV_MAC_TAGS_CFG_VLAN_AWR_ENA_SET(port->vlan_aware) | |
173 | DEV_MAC_TAGS_CFG_VLAN_DBL_AWR_ENA_SET(port->vlan_aware), |
174 | DEV_MAC_TAGS_CFG_VLAN_AWR_ENA | |
175 | DEV_MAC_TAGS_CFG_VLAN_DBL_AWR_ENA, |
176 | lan966x, DEV_MAC_TAGS_CFG(port->chip_port)); |
177 | |
178 | /* Drop frames with multicast source address */ |
179 | val = ANA_DROP_CFG_DROP_MC_SMAC_ENA_SET(1); |
180 | if (port->vlan_aware && !pvid) |
181 | /* If port is vlan-aware and tagged, drop untagged and priority |
182 | * tagged frames. |
183 | */ |
184 | val |= ANA_DROP_CFG_DROP_UNTAGGED_ENA_SET(1) | |
185 | ANA_DROP_CFG_DROP_PRIO_S_TAGGED_ENA_SET(1) | |
186 | ANA_DROP_CFG_DROP_PRIO_C_TAGGED_ENA_SET(1); |
187 | |
188 | lan_wr(val, lan966x, ANA_DROP_CFG(port->chip_port)); |
189 | |
190 | /* Egress configuration (REW_TAG_CFG): VLAN tag type to 8021Q */ |
191 | val = REW_TAG_CFG_TAG_TPID_CFG_SET(0); |
192 | if (port->vlan_aware) { |
193 | if (port->vid) |
194 | /* Tag all frames except when VID == DEFAULT_VLAN */ |
195 | val |= REW_TAG_CFG_TAG_CFG_SET(1); |
196 | else |
197 | val |= REW_TAG_CFG_TAG_CFG_SET(3); |
198 | } |
199 | |
200 | /* Update only some bits in the register */ |
201 | lan_rmw(val, |
202 | REW_TAG_CFG_TAG_TPID_CFG | REW_TAG_CFG_TAG_CFG, |
203 | lan966x, REW_TAG_CFG(port->chip_port)); |
204 | |
205 | /* Set default VLAN and tag type to 8021Q */ |
206 | lan_rmw(REW_PORT_VLAN_CFG_PORT_TPID_SET(ETH_P_8021Q) | |
207 | REW_PORT_VLAN_CFG_PORT_VID_SET(port->vid), |
208 | REW_PORT_VLAN_CFG_PORT_TPID | |
209 | REW_PORT_VLAN_CFG_PORT_VID, |
210 | lan966x, REW_PORT_VLAN_CFG(port->chip_port)); |
211 | } |
212 | |
213 | void lan966x_vlan_port_add_vlan(struct lan966x_port *port, |
214 | u16 vid, |
215 | bool pvid, |
216 | bool untagged) |
217 | { |
218 | struct lan966x *lan966x = port->lan966x; |
219 | |
220 | /* If the CPU(br) is already part of the vlan then add the fdb |
221 | * entries in MAC table to copy the frames to the CPU(br). |
222 | * If the CPU(br) is not part of the vlan then it would |
223 | * just drop the frames. |
224 | */ |
225 | if (lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x, vid)) { |
226 | lan966x_vlan_cpu_add_vlan_mask(lan966x, vid); |
227 | lan966x_fdb_write_entries(lan966x, vid); |
228 | lan966x_mdb_write_entries(lan966x, vid); |
229 | } |
230 | |
231 | lan966x_vlan_port_set_vid(port, vid, pvid, untagged); |
232 | lan966x_vlan_port_add_vlan_mask(port, vid); |
233 | lan966x_vlan_port_apply(port); |
234 | } |
235 | |
236 | void lan966x_vlan_port_del_vlan(struct lan966x_port *port, u16 vid) |
237 | { |
238 | struct lan966x *lan966x = port->lan966x; |
239 | |
240 | lan966x_vlan_port_remove_vid(port, vid); |
241 | lan966x_vlan_port_del_vlan_mask(port, vid); |
242 | lan966x_vlan_port_apply(port); |
243 | |
244 | /* In case there are no other ports in vlan then remove the CPU from |
245 | * that vlan but still keep it in the mask because it may be needed |
246 | * again then another port gets added in that vlan |
247 | */ |
248 | if (!lan966x_vlan_port_any_vlan_mask(lan966x, vid)) { |
249 | lan966x_vlan_cpu_del_vlan_mask(lan966x, vid); |
250 | lan966x_fdb_erase_entries(lan966x, vid); |
251 | lan966x_mdb_erase_entries(lan966x, vid); |
252 | } |
253 | } |
254 | |
255 | void lan966x_vlan_cpu_add_vlan(struct lan966x *lan966x, u16 vid) |
256 | { |
257 | /* Add an entry in the MAC table for the CPU |
258 | * Add the CPU part of the vlan only if there is another port in that |
259 | * vlan otherwise all the broadcast frames in that vlan will go to CPU |
260 | * even if none of the ports are in the vlan and then the CPU will just |
261 | * need to discard these frames. It is required to store this |
262 | * information so when a front port is added then it would add also the |
263 | * CPU port. |
264 | */ |
265 | if (lan966x_vlan_port_any_vlan_mask(lan966x, vid)) { |
266 | lan966x_vlan_cpu_add_vlan_mask(lan966x, vid); |
267 | lan966x_mdb_write_entries(lan966x, vid); |
268 | } |
269 | |
270 | lan966x_vlan_cpu_add_cpu_vlan_mask(lan966x, vid); |
271 | lan966x_fdb_write_entries(lan966x, vid); |
272 | } |
273 | |
274 | void lan966x_vlan_cpu_del_vlan(struct lan966x *lan966x, u16 vid) |
275 | { |
276 | /* Remove the CPU part of the vlan */ |
277 | lan966x_vlan_cpu_del_cpu_vlan_mask(lan966x, vid); |
278 | lan966x_vlan_cpu_del_vlan_mask(lan966x, vid); |
279 | lan966x_fdb_erase_entries(lan966x, vid); |
280 | lan966x_mdb_erase_entries(lan966x, vid); |
281 | } |
282 | |
283 | void lan966x_vlan_init(struct lan966x *lan966x) |
284 | { |
285 | u16 port, vid; |
286 | |
287 | /* Clear VLAN table, by default all ports are members of all VLANS */ |
288 | lan_rmw(ANA_VLANACCESS_VLAN_TBL_CMD_SET(VLANACCESS_CMD_INIT), |
289 | ANA_VLANACCESS_VLAN_TBL_CMD, |
290 | lan966x, ANA_VLANACCESS); |
291 | lan966x_vlan_wait_for_completion(lan966x); |
292 | |
293 | for (vid = 1; vid < VLAN_N_VID; vid++) { |
294 | lan966x->vlan_mask[vid] = 0; |
295 | lan966x_vlan_set_mask(lan966x, vid); |
296 | } |
297 | |
298 | /* Set all the ports + cpu to be part of HOST_PVID and UNAWARE_PVID */ |
299 | lan966x->vlan_mask[HOST_PVID] = |
300 | GENMASK(lan966x->num_phys_ports - 1, 0) | BIT(CPU_PORT); |
301 | lan966x_vlan_set_mask(lan966x, HOST_PVID); |
302 | |
303 | lan966x->vlan_mask[UNAWARE_PVID] = |
304 | GENMASK(lan966x->num_phys_ports - 1, 0) | BIT(CPU_PORT); |
305 | lan966x_vlan_set_mask(lan966x, UNAWARE_PVID); |
306 | |
307 | lan966x_vlan_cpu_add_cpu_vlan_mask(lan966x, UNAWARE_PVID); |
308 | |
309 | /* Configure the CPU port to be vlan aware */ |
310 | lan_wr(ANA_VLAN_CFG_VLAN_VID_SET(0) | |
311 | ANA_VLAN_CFG_VLAN_AWARE_ENA_SET(1) | |
312 | ANA_VLAN_CFG_VLAN_POP_CNT_SET(1), |
313 | lan966x, ANA_VLAN_CFG(CPU_PORT)); |
314 | |
315 | /* Set vlan ingress filter mask to all ports */ |
316 | lan_wr(GENMASK(lan966x->num_phys_ports, 0), |
317 | lan966x, ANA_VLANMASK); |
318 | |
319 | for (port = 0; port < lan966x->num_phys_ports; port++) { |
320 | lan_wr(val: 0, lan966x, REW_PORT_VLAN_CFG(port)); |
321 | lan_wr(val: 0, lan966x, REW_TAG_CFG(port)); |
322 | } |
323 | } |
324 | |