File Index
Files and directories
Sign in
FILE

lan966x_vlan.c

File preview with syntax highlighting.

linux-6.19.14/drivers/net/ethernet/microchip/lan966x/lan966x_vlan.c 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
 10static int lan966x_vlan_get_status(struct lan966x *lan966x)
 11{
 12	return lan_rd(lan966x, ANA_VLANACCESS);
 13}
 14
 15static 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
 26static 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
 54static 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
 63static 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
 72static bool lan966x_vlan_port_any_vlan_mask(struct lan966x *lan966x, u16 vid)
 73{
 74	return !!(lan966x->vlan_mask[vid] & ~BIT(CPU_PORT));
 75}
 76
 77static 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
 83static 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
 89static void lan966x_vlan_cpu_add_cpu_vlan_mask(struct lan966x *lan966x, u16 vid)
 90{
 91	__set_bit(vid, lan966x->cpu_vlan_mask);
 92}
 93
 94static void lan966x_vlan_cpu_del_cpu_vlan_mask(struct lan966x *lan966x, u16 vid)
 95{
 96	__clear_bit(vid, lan966x->cpu_vlan_mask);
 97}
 98
 99bool lan966x_vlan_cpu_member_cpu_vlan_mask(struct lan966x *lan966x, u16 vid)
100{
101	return test_bit(vid, lan966x->cpu_vlan_mask);
102}
103
104static 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
114int 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
137static 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
146void lan966x_vlan_port_set_vlan_aware(struct lan966x_port *port,
147				      bool vlan_aware)
148{
149	port->vlan_aware = vlan_aware;
150}
151
152/* When the interface is in host mode, the interface should not be vlan aware
153 * but it should insert all the tags that it gets from the network stack.
154 * The tags are not in the data of the frame but actually in the skb and the ifh
155 * is configured already to get this tag. So what we need to do is to update the
156 * rewriter to insert the vlan tag for all frames which have a vlan tag
157 * different than 0.
158 */
159void lan966x_vlan_port_rew_host(struct lan966x_port *port)
160{
161	struct lan966x *lan966x = port->lan966x;
162	u32 val;
163
164	/* Tag all frames except when VID=0*/
165	val = REW_TAG_CFG_TAG_CFG_SET(2);
166
167	/* Update only some bits in the register */
168	lan_rmw(val,
169		REW_TAG_CFG_TAG_CFG,
170		lan966x, REW_TAG_CFG(port->chip_port));
171}
172
173void lan966x_vlan_port_apply(struct lan966x_port *port)
174{
175	struct lan966x *lan966x = port->lan966x;
176	u16 pvid;
177	u32 val;
178
179	pvid = lan966x_vlan_port_get_pvid(port);
180
181	/* Ingress classification (ANA_PORT_VLAN_CFG) */
182	/* Default vlan to classify for untagged frames (may be zero) */
183	val = ANA_VLAN_CFG_VLAN_VID_SET(pvid);
184	if (port->vlan_aware)
185		val |= ANA_VLAN_CFG_VLAN_AWARE_ENA_SET(1) |
186		       ANA_VLAN_CFG_VLAN_POP_CNT_SET(1);
187
188	lan_rmw(val,
189		ANA_VLAN_CFG_VLAN_VID | ANA_VLAN_CFG_VLAN_AWARE_ENA |
190		ANA_VLAN_CFG_VLAN_POP_CNT,
191		lan966x, ANA_VLAN_CFG(port->chip_port));
192
193	lan_rmw(DEV_MAC_TAGS_CFG_VLAN_AWR_ENA_SET(port->vlan_aware) |
194		DEV_MAC_TAGS_CFG_VLAN_DBL_AWR_ENA_SET(port->vlan_aware),
195		DEV_MAC_TAGS_CFG_VLAN_AWR_ENA |
196		DEV_MAC_TAGS_CFG_VLAN_DBL_AWR_ENA,
197		lan966x, DEV_MAC_TAGS_CFG(port->chip_port));
198
199	/* Drop frames with multicast source address */
200	val = ANA_DROP_CFG_DROP_MC_SMAC_ENA_SET(1);
201	if (port->vlan_aware && !pvid)
202		/* If port is vlan-aware and tagged, drop untagged and priority
203		 * tagged frames.
204		 */
205		val |= ANA_DROP_CFG_DROP_UNTAGGED_ENA_SET(1) |
206		       ANA_DROP_CFG_DROP_PRIO_S_TAGGED_ENA_SET(1) |
207		       ANA_DROP_CFG_DROP_PRIO_C_TAGGED_ENA_SET(1);
208
209	lan_wr(val, lan966x, ANA_DROP_CFG(port->chip_port));
210
211	/* Egress configuration (REW_TAG_CFG): VLAN tag type to 8021Q */
212	val = REW_TAG_CFG_TAG_TPID_CFG_SET(0);
213	if (port->vlan_aware) {
214		if (port->vid)
215			/* Tag all frames except when VID == DEFAULT_VLAN */
216			val |= REW_TAG_CFG_TAG_CFG_SET(1);
217		else
218			val |= REW_TAG_CFG_TAG_CFG_SET(3);
219	}
220
221	/* Update only some bits in the register */
222	lan_rmw(val,
223		REW_TAG_CFG_TAG_TPID_CFG | REW_TAG_CFG_TAG_CFG,
224		lan966x, REW_TAG_CFG(port->chip_port));
225
226	/* Set default VLAN and tag type to 8021Q */
227	lan_rmw(REW_PORT_VLAN_CFG_PORT_TPID_SET(ETH_P_8021Q) |
228		REW_PORT_VLAN_CFG_PORT_VID_SET(port->vid),
229		REW_PORT_VLAN_CFG_PORT_TPID |
230		REW_PORT_VLAN_CFG_PORT_VID,
231		lan966x, REW_PORT_VLAN_CFG(port->chip_port));
232}
233
234void lan966x_vlan_port_add_vlan(struct lan966x_port *port,
235				u16 vid,
236				bool pvid,
237				bool untagged)
238{
239	struct lan966x *lan966x = port->lan966x;
240
241	/* If the CPU(br) is already part of the vlan then add the fdb
242	 * entries in MAC table to copy the frames to the CPU(br).
243	 * If the CPU(br) is not part of the vlan then it would
244	 * just drop the frames.
245	 */
246	if (lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x, vid)) {
247		lan966x_vlan_cpu_add_vlan_mask(lan966x, vid);
248		lan966x_fdb_write_entries(lan966x, vid);
249		lan966x_mdb_write_entries(lan966x, vid);
250	}
251
252	lan966x_vlan_port_set_vid(port, vid, pvid, untagged);
253	lan966x_vlan_port_add_vlan_mask(port, vid);
254	lan966x_vlan_port_apply(port);
255}
256
257void lan966x_vlan_port_del_vlan(struct lan966x_port *port, u16 vid)
258{
259	struct lan966x *lan966x = port->lan966x;
260
261	lan966x_vlan_port_remove_vid(port, vid);
262	lan966x_vlan_port_del_vlan_mask(port, vid);
263	lan966x_vlan_port_apply(port);
264
265	/* In case there are no other ports in vlan then remove the CPU from
266	 * that vlan but still keep it in the mask because it may be needed
267	 * again then another port gets added in that vlan
268	 */
269	if (!lan966x_vlan_port_any_vlan_mask(lan966x, vid)) {
270		lan966x_vlan_cpu_del_vlan_mask(lan966x, vid);
271		lan966x_fdb_erase_entries(lan966x, vid);
272		lan966x_mdb_erase_entries(lan966x, vid);
273	}
274}
275
276void lan966x_vlan_cpu_add_vlan(struct lan966x *lan966x, u16 vid)
277{
278	/* Add an entry in the MAC table for the CPU
279	 * Add the CPU part of the vlan only if there is another port in that
280	 * vlan otherwise all the broadcast frames in that vlan will go to CPU
281	 * even if none of the ports are in the vlan and then the CPU will just
282	 * need to discard these frames. It is required to store this
283	 * information so when a front port is added then it would add also the
284	 * CPU port.
285	 */
286	if (lan966x_vlan_port_any_vlan_mask(lan966x, vid)) {
287		lan966x_vlan_cpu_add_vlan_mask(lan966x, vid);
288		lan966x_mdb_write_entries(lan966x, vid);
289	}
290
291	lan966x_vlan_cpu_add_cpu_vlan_mask(lan966x, vid);
292	lan966x_fdb_write_entries(lan966x, vid);
293}
294
295void lan966x_vlan_cpu_del_vlan(struct lan966x *lan966x, u16 vid)
296{
297	/* Remove the CPU part of the vlan */
298	lan966x_vlan_cpu_del_cpu_vlan_mask(lan966x, vid);
299	lan966x_vlan_cpu_del_vlan_mask(lan966x, vid);
300	lan966x_fdb_erase_entries(lan966x, vid);
301	lan966x_mdb_erase_entries(lan966x, vid);
302}
303
304void lan966x_vlan_init(struct lan966x *lan966x)
305{
306	u16 port, vid;
307
308	/* Clear VLAN table, by default all ports are members of all VLANS */
309	lan_rmw(ANA_VLANACCESS_VLAN_TBL_CMD_SET(VLANACCESS_CMD_INIT),
310		ANA_VLANACCESS_VLAN_TBL_CMD,
311		lan966x, ANA_VLANACCESS);
312	lan966x_vlan_wait_for_completion(lan966x);
313
314	for (vid = 1; vid < VLAN_N_VID; vid++) {
315		lan966x->vlan_mask[vid] = 0;
316		lan966x_vlan_set_mask(lan966x, vid);
317	}
318
319	/* Set all the ports + cpu to be part of HOST_PVID and UNAWARE_PVID */
320	lan966x->vlan_mask[HOST_PVID] =
321		GENMASK(lan966x->num_phys_ports - 1, 0) | BIT(CPU_PORT);
322	lan966x_vlan_set_mask(lan966x, HOST_PVID);
323
324	lan966x->vlan_mask[UNAWARE_PVID] =
325		GENMASK(lan966x->num_phys_ports - 1, 0) | BIT(CPU_PORT);
326	lan966x_vlan_set_mask(lan966x, UNAWARE_PVID);
327
328	lan966x_vlan_cpu_add_cpu_vlan_mask(lan966x, UNAWARE_PVID);
329
330	/* Configure the CPU port to be vlan aware */
331	lan_wr(ANA_VLAN_CFG_VLAN_VID_SET(0) |
332	       ANA_VLAN_CFG_VLAN_AWARE_ENA_SET(1) |
333	       ANA_VLAN_CFG_VLAN_POP_CNT_SET(1),
334	       lan966x, ANA_VLAN_CFG(CPU_PORT));
335
336	/* Set vlan ingress filter mask to all ports */
337	lan_wr(GENMASK(lan966x->num_phys_ports, 0),
338	       lan966x, ANA_VLANMASK);
339
340	for (port = 0; port < lan966x->num_phys_ports; port++) {
341		lan_wr(0, lan966x, REW_PORT_VLAN_CFG(port));
342		lan_wr(0, lan966x, REW_TAG_CFG(port));
343	}
344}
~/ Read-only file preview.