]> Git Repo - J-linux.git/blob - drivers/net/ethernet/microchip/lan966x/lan966x_vlan.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / drivers / net / ethernet / microchip / lan966x / lan966x_vlan.c
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 classification (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(0, lan966x, REW_PORT_VLAN_CFG(port));
321                 lan_wr(0, lan966x, REW_TAG_CFG(port));
322         }
323 }
This page took 0.046794 seconds and 4 git commands to generate.