xref: /openbmc/linux/drivers/net/ethernet/microchip/lan966x/lan966x_vlan.c (revision 7ae9fb1b7ecbb5d85d07857943f677fd1a559b18)
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  
lan966x_vlan_get_status(struct lan966x * lan966x)10  static int lan966x_vlan_get_status(struct lan966x *lan966x)
11  {
12  	return lan_rd(lan966x, ANA_VLANACCESS);
13  }
14  
lan966x_vlan_wait_for_completion(struct lan966x * lan966x)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  
lan966x_vlan_set_mask(struct lan966x * lan966x,u16 vid)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  
lan966x_vlan_port_add_vlan_mask(struct lan966x_port * port,u16 vid)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  
lan966x_vlan_port_del_vlan_mask(struct lan966x_port * port,u16 vid)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  
lan966x_vlan_port_any_vlan_mask(struct lan966x * lan966x,u16 vid)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  
lan966x_vlan_cpu_add_vlan_mask(struct lan966x * lan966x,u16 vid)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  
lan966x_vlan_cpu_del_vlan_mask(struct lan966x * lan966x,u16 vid)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  
lan966x_vlan_cpu_add_cpu_vlan_mask(struct lan966x * lan966x,u16 vid)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  
lan966x_vlan_cpu_del_cpu_vlan_mask(struct lan966x * lan966x,u16 vid)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  
lan966x_vlan_cpu_member_cpu_vlan_mask(struct lan966x * lan966x,u16 vid)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  
lan966x_vlan_port_get_pvid(struct lan966x_port * port)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  
lan966x_vlan_port_set_vid(struct lan966x_port * port,u16 vid,bool pvid,bool untagged)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  
lan966x_vlan_port_remove_vid(struct lan966x_port * port,u16 vid)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  
lan966x_vlan_port_set_vlan_aware(struct lan966x_port * port,bool vlan_aware)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  
lan966x_vlan_port_apply(struct lan966x_port * port)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  
lan966x_vlan_port_add_vlan(struct lan966x_port * port,u16 vid,bool pvid,bool untagged)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  
lan966x_vlan_port_del_vlan(struct lan966x_port * port,u16 vid)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  
lan966x_vlan_cpu_add_vlan(struct lan966x * lan966x,u16 vid)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  
lan966x_vlan_cpu_del_vlan(struct lan966x * lan966x,u16 vid)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  
lan966x_vlan_init(struct lan966x * lan966x)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  }
324