xref: /openbmc/linux/drivers/net/ethernet/microchip/sparx5/sparx5_mactable.c (revision 22a41e9a5044bf3519f05b4a00e99af34bfeb40c)
1 // SPDX-License-Identifier: GPL-2.0+
2 /* Microchip Sparx5 Switch driver
3  *
4  * Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.
5  */
6 
7 #include <net/switchdev.h>
8 #include <linux/if_bridge.h>
9 #include <linux/iopoll.h>
10 
11 #include "sparx5_main_regs.h"
12 #include "sparx5_main.h"
13 
14 /* Commands for Mac Table Command register */
15 #define MAC_CMD_LEARN         0 /* Insert (Learn) 1 entry */
16 #define MAC_CMD_UNLEARN       1 /* Unlearn (Forget) 1 entry */
17 #define MAC_CMD_LOOKUP        2 /* Look up 1 entry */
18 #define MAC_CMD_READ          3 /* Read entry at Mac Table Index */
19 #define MAC_CMD_WRITE         4 /* Write entry at Mac Table Index */
20 #define MAC_CMD_SCAN          5 /* Scan (Age or find next) */
21 #define MAC_CMD_FIND_SMALLEST 6 /* Get next entry */
22 #define MAC_CMD_CLEAR_ALL     7 /* Delete all entries in table */
23 
24 /* Commands for MAC_ENTRY_ADDR_TYPE */
25 #define  MAC_ENTRY_ADDR_TYPE_UPSID_PN         0
26 #define  MAC_ENTRY_ADDR_TYPE_UPSID_CPU_OR_INT 1
27 #define  MAC_ENTRY_ADDR_TYPE_GLAG             2
28 #define  MAC_ENTRY_ADDR_TYPE_MC_IDX           3
29 
30 #define TABLE_UPDATE_SLEEP_US 10
31 #define TABLE_UPDATE_TIMEOUT_US 100000
32 
33 struct sparx5_mact_entry {
34 	struct list_head list;
35 	unsigned char mac[ETH_ALEN];
36 	u32 flags;
37 #define MAC_ENT_ALIVE	BIT(0)
38 #define MAC_ENT_MOVED	BIT(1)
39 #define MAC_ENT_LOCK	BIT(2)
40 	u16 vid;
41 	u16 port;
42 };
43 
44 static int sparx5_mact_get_status(struct sparx5 *sparx5)
45 {
46 	return spx5_rd(sparx5, LRN_COMMON_ACCESS_CTRL);
47 }
48 
49 static int sparx5_mact_wait_for_completion(struct sparx5 *sparx5)
50 {
51 	u32 val;
52 
53 	return readx_poll_timeout(sparx5_mact_get_status,
54 		sparx5, val,
55 		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_GET(val) == 0,
56 		TABLE_UPDATE_SLEEP_US, TABLE_UPDATE_TIMEOUT_US);
57 }
58 
59 static void sparx5_mact_select(struct sparx5 *sparx5,
60 			       const unsigned char mac[ETH_ALEN],
61 			       u16 vid)
62 {
63 	u32 macl = 0, mach = 0;
64 
65 	/* Set the MAC address to handle and the vlan associated in a format
66 	 * understood by the hardware.
67 	 */
68 	mach |= vid    << 16;
69 	mach |= mac[0] << 8;
70 	mach |= mac[1] << 0;
71 	macl |= mac[2] << 24;
72 	macl |= mac[3] << 16;
73 	macl |= mac[4] << 8;
74 	macl |= mac[5] << 0;
75 
76 	spx5_wr(mach, sparx5, LRN_MAC_ACCESS_CFG_0);
77 	spx5_wr(macl, sparx5, LRN_MAC_ACCESS_CFG_1);
78 }
79 
80 int sparx5_mact_learn(struct sparx5 *sparx5, int pgid,
81 		      const unsigned char mac[ETH_ALEN], u16 vid)
82 {
83 	int addr, type, ret;
84 
85 	if (pgid < SPX5_PORTS) {
86 		type = MAC_ENTRY_ADDR_TYPE_UPSID_PN;
87 		addr = pgid % 32;
88 		addr += (pgid / 32) << 5; /* Add upsid */
89 	} else {
90 		type = MAC_ENTRY_ADDR_TYPE_MC_IDX;
91 		addr = pgid - SPX5_PORTS;
92 	}
93 
94 	mutex_lock(&sparx5->lock);
95 
96 	sparx5_mact_select(sparx5, mac, vid);
97 
98 	/* MAC entry properties */
99 	spx5_wr(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_SET(addr) |
100 		LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_TYPE_SET(type) |
101 		LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_SET(1) |
102 		LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_LOCKED_SET(1),
103 		sparx5, LRN_MAC_ACCESS_CFG_2);
104 	spx5_wr(0, sparx5, LRN_MAC_ACCESS_CFG_3);
105 
106 	/*  Insert/learn new entry */
107 	spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_LEARN) |
108 		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
109 		sparx5, LRN_COMMON_ACCESS_CTRL);
110 
111 	ret = sparx5_mact_wait_for_completion(sparx5);
112 
113 	mutex_unlock(&sparx5->lock);
114 
115 	return ret;
116 }
117 
118 int sparx5_mc_unsync(struct net_device *dev, const unsigned char *addr)
119 {
120 	struct sparx5_port *port = netdev_priv(dev);
121 	struct sparx5 *sparx5 = port->sparx5;
122 
123 	return sparx5_mact_forget(sparx5, addr, port->pvid);
124 }
125 
126 int sparx5_mc_sync(struct net_device *dev, const unsigned char *addr)
127 {
128 	struct sparx5_port *port = netdev_priv(dev);
129 	struct sparx5 *sparx5 = port->sparx5;
130 
131 	return sparx5_mact_learn(sparx5, PGID_CPU, addr, port->pvid);
132 }
133 
134 static int sparx5_mact_get(struct sparx5 *sparx5,
135 			   unsigned char mac[ETH_ALEN],
136 			   u16 *vid, u32 *pcfg2)
137 {
138 	u32 mach, macl, cfg2;
139 	int ret = -ENOENT;
140 
141 	cfg2 = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_2);
142 	if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_GET(cfg2)) {
143 		mach = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_0);
144 		macl = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_1);
145 		mac[0] = ((mach >> 8)  & 0xff);
146 		mac[1] = ((mach >> 0)  & 0xff);
147 		mac[2] = ((macl >> 24) & 0xff);
148 		mac[3] = ((macl >> 16) & 0xff);
149 		mac[4] = ((macl >> 8)  & 0xff);
150 		mac[5] = ((macl >> 0)  & 0xff);
151 		*vid = mach >> 16;
152 		*pcfg2 = cfg2;
153 		ret = 0;
154 	}
155 
156 	return ret;
157 }
158 
159 bool sparx5_mact_getnext(struct sparx5 *sparx5,
160 			 unsigned char mac[ETH_ALEN], u16 *vid, u32 *pcfg2)
161 {
162 	u32 cfg2;
163 	int ret;
164 
165 	mutex_lock(&sparx5->lock);
166 
167 	sparx5_mact_select(sparx5, mac, *vid);
168 
169 	spx5_wr(LRN_SCAN_NEXT_CFG_SCAN_NEXT_IGNORE_LOCKED_ENA_SET(1) |
170 		LRN_SCAN_NEXT_CFG_SCAN_NEXT_UNTIL_FOUND_ENA_SET(1),
171 		sparx5, LRN_SCAN_NEXT_CFG);
172 	spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET
173 		(MAC_CMD_FIND_SMALLEST) |
174 		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
175 		sparx5, LRN_COMMON_ACCESS_CTRL);
176 
177 	ret = sparx5_mact_wait_for_completion(sparx5);
178 	if (ret == 0) {
179 		ret = sparx5_mact_get(sparx5, mac, vid, &cfg2);
180 		if (ret == 0)
181 			*pcfg2 = cfg2;
182 	}
183 
184 	mutex_unlock(&sparx5->lock);
185 
186 	return ret == 0;
187 }
188 
189 bool sparx5_mact_find(struct sparx5 *sparx5,
190 		      const unsigned char mac[ETH_ALEN], u16 vid, u32 *pcfg2)
191 {
192 	int ret;
193 	u32 cfg2;
194 
195 	mutex_lock(&sparx5->lock);
196 
197 	sparx5_mact_select(sparx5, mac, vid);
198 
199 	/* Issue a lookup command */
200 	spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_LOOKUP) |
201 		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
202 		sparx5, LRN_COMMON_ACCESS_CTRL);
203 
204 	ret = sparx5_mact_wait_for_completion(sparx5);
205 	if (ret == 0) {
206 		cfg2 = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_2);
207 		if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_GET(cfg2))
208 			*pcfg2 = cfg2;
209 		else
210 			ret = -ENOENT;
211 	}
212 
213 	mutex_unlock(&sparx5->lock);
214 
215 	return ret == 0;
216 }
217 
218 static int sparx5_mact_lookup(struct sparx5 *sparx5,
219 			      const unsigned char mac[ETH_ALEN],
220 			      u16 vid)
221 {
222 	u32 pcfg2;
223 
224 	if (sparx5_mact_find(sparx5, mac, vid, &pcfg2))
225 		return 1;
226 
227 	return 0;
228 }
229 
230 int sparx5_mact_forget(struct sparx5 *sparx5,
231 		       const unsigned char mac[ETH_ALEN], u16 vid)
232 {
233 	int ret;
234 
235 	mutex_lock(&sparx5->lock);
236 
237 	sparx5_mact_select(sparx5, mac, vid);
238 
239 	/* Issue an unlearn command */
240 	spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_UNLEARN) |
241 		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
242 		sparx5, LRN_COMMON_ACCESS_CTRL);
243 
244 	ret = sparx5_mact_wait_for_completion(sparx5);
245 
246 	mutex_unlock(&sparx5->lock);
247 
248 	return ret;
249 }
250 
251 static struct sparx5_mact_entry *alloc_mact_entry(struct sparx5 *sparx5,
252 						  const unsigned char *mac,
253 						  u16 vid, u16 port_index)
254 {
255 	struct sparx5_mact_entry *mact_entry;
256 
257 	mact_entry = devm_kzalloc(sparx5->dev,
258 				  sizeof(*mact_entry), GFP_ATOMIC);
259 	if (!mact_entry)
260 		return NULL;
261 
262 	memcpy(mact_entry->mac, mac, ETH_ALEN);
263 	mact_entry->vid = vid;
264 	mact_entry->port = port_index;
265 	return mact_entry;
266 }
267 
268 static struct sparx5_mact_entry *find_mact_entry(struct sparx5 *sparx5,
269 						 const unsigned char *mac,
270 						 u16 vid, u16 port_index)
271 {
272 	struct sparx5_mact_entry *mact_entry;
273 	struct sparx5_mact_entry *res = NULL;
274 
275 	mutex_lock(&sparx5->mact_lock);
276 	list_for_each_entry(mact_entry, &sparx5->mact_entries, list) {
277 		if (mact_entry->vid == vid &&
278 		    ether_addr_equal(mac, mact_entry->mac) &&
279 		    mact_entry->port == port_index) {
280 			res = mact_entry;
281 			break;
282 		}
283 	}
284 	mutex_unlock(&sparx5->mact_lock);
285 
286 	return res;
287 }
288 
289 static void sparx5_fdb_call_notifiers(enum switchdev_notifier_type type,
290 				      const char *mac, u16 vid,
291 				      struct net_device *dev, bool offloaded)
292 {
293 	struct switchdev_notifier_fdb_info info = {};
294 
295 	info.addr = mac;
296 	info.vid = vid;
297 	info.offloaded = offloaded;
298 	call_switchdev_notifiers(type, dev, &info.info, NULL);
299 }
300 
301 int sparx5_add_mact_entry(struct sparx5 *sparx5,
302 			  struct net_device *dev,
303 			  u16 portno,
304 			  const unsigned char *addr, u16 vid)
305 {
306 	struct sparx5_mact_entry *mact_entry;
307 	int ret;
308 
309 	ret = sparx5_mact_lookup(sparx5, addr, vid);
310 	if (ret)
311 		return 0;
312 
313 	/* In case the entry already exists, don't add it again to SW,
314 	 * just update HW, but we need to look in the actual HW because
315 	 * it is possible for an entry to be learn by HW and before the
316 	 * mact thread to start the frame will reach CPU and the CPU will
317 	 * add the entry but without the extern_learn flag.
318 	 */
319 	mact_entry = find_mact_entry(sparx5, addr, vid, portno);
320 	if (mact_entry)
321 		goto update_hw;
322 
323 	/* Add the entry in SW MAC table not to get the notification when
324 	 * SW is pulling again
325 	 */
326 	mact_entry = alloc_mact_entry(sparx5, addr, vid, portno);
327 	if (!mact_entry)
328 		return -ENOMEM;
329 
330 	mutex_lock(&sparx5->mact_lock);
331 	list_add_tail(&mact_entry->list, &sparx5->mact_entries);
332 	mutex_unlock(&sparx5->mact_lock);
333 
334 update_hw:
335 	ret = sparx5_mact_learn(sparx5, portno, addr, vid);
336 
337 	/* New entry? */
338 	if (mact_entry->flags == 0) {
339 		mact_entry->flags |= MAC_ENT_LOCK; /* Don't age this */
340 		sparx5_fdb_call_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, addr, vid,
341 					  dev, true);
342 	}
343 
344 	return ret;
345 }
346 
347 int sparx5_del_mact_entry(struct sparx5 *sparx5,
348 			  const unsigned char *addr,
349 			  u16 vid)
350 {
351 	struct sparx5_mact_entry *mact_entry, *tmp;
352 
353 	/* Delete the entry in SW MAC table not to get the notification when
354 	 * SW is pulling again
355 	 */
356 	mutex_lock(&sparx5->mact_lock);
357 	list_for_each_entry_safe(mact_entry, tmp, &sparx5->mact_entries,
358 				 list) {
359 		if ((vid == 0 || mact_entry->vid == vid) &&
360 		    ether_addr_equal(addr, mact_entry->mac)) {
361 			list_del(&mact_entry->list);
362 			devm_kfree(sparx5->dev, mact_entry);
363 
364 			sparx5_mact_forget(sparx5, addr, mact_entry->vid);
365 		}
366 	}
367 	mutex_unlock(&sparx5->mact_lock);
368 
369 	return 0;
370 }
371 
372 static void sparx5_mact_handle_entry(struct sparx5 *sparx5,
373 				     unsigned char mac[ETH_ALEN],
374 				     u16 vid, u32 cfg2)
375 {
376 	struct sparx5_mact_entry *mact_entry;
377 	bool found = false;
378 	u16 port;
379 
380 	if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_TYPE_GET(cfg2) !=
381 	    MAC_ENTRY_ADDR_TYPE_UPSID_PN)
382 		return;
383 
384 	port = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_GET(cfg2);
385 	if (port >= SPX5_PORTS)
386 		return;
387 
388 	if (!test_bit(port, sparx5->bridge_mask))
389 		return;
390 
391 	mutex_lock(&sparx5->mact_lock);
392 	list_for_each_entry(mact_entry, &sparx5->mact_entries, list) {
393 		if (mact_entry->vid == vid &&
394 		    ether_addr_equal(mac, mact_entry->mac)) {
395 			found = true;
396 			mact_entry->flags |= MAC_ENT_ALIVE;
397 			if (mact_entry->port != port) {
398 				dev_warn(sparx5->dev, "Entry move: %d -> %d\n",
399 					 mact_entry->port, port);
400 				mact_entry->port = port;
401 				mact_entry->flags |= MAC_ENT_MOVED;
402 			}
403 			/* Entry handled */
404 			break;
405 		}
406 	}
407 	mutex_unlock(&sparx5->mact_lock);
408 
409 	if (found && !(mact_entry->flags & MAC_ENT_MOVED))
410 		/* Present, not moved */
411 		return;
412 
413 	if (!found) {
414 		/* Entry not found - now add */
415 		mact_entry = alloc_mact_entry(sparx5, mac, vid, port);
416 		if (!mact_entry)
417 			return;
418 
419 		mact_entry->flags |= MAC_ENT_ALIVE;
420 		mutex_lock(&sparx5->mact_lock);
421 		list_add_tail(&mact_entry->list, &sparx5->mact_entries);
422 		mutex_unlock(&sparx5->mact_lock);
423 	}
424 
425 	/* New or moved entry - notify bridge */
426 	sparx5_fdb_call_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE,
427 				  mac, vid, sparx5->ports[port]->ndev,
428 				  true);
429 }
430 
431 void sparx5_mact_pull_work(struct work_struct *work)
432 {
433 	struct delayed_work *del_work = to_delayed_work(work);
434 	struct sparx5 *sparx5 = container_of(del_work, struct sparx5,
435 					     mact_work);
436 	struct sparx5_mact_entry *mact_entry, *tmp;
437 	unsigned char mac[ETH_ALEN];
438 	u32 cfg2;
439 	u16 vid;
440 	int ret;
441 
442 	/* Reset MAC entry flags */
443 	mutex_lock(&sparx5->mact_lock);
444 	list_for_each_entry(mact_entry, &sparx5->mact_entries, list)
445 		mact_entry->flags &= MAC_ENT_LOCK;
446 	mutex_unlock(&sparx5->mact_lock);
447 
448 	/* MAIN mac address processing loop */
449 	vid = 0;
450 	memset(mac, 0, sizeof(mac));
451 	do {
452 		mutex_lock(&sparx5->lock);
453 		sparx5_mact_select(sparx5, mac, vid);
454 		spx5_wr(LRN_SCAN_NEXT_CFG_SCAN_NEXT_UNTIL_FOUND_ENA_SET(1),
455 			sparx5, LRN_SCAN_NEXT_CFG);
456 		spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET
457 			(MAC_CMD_FIND_SMALLEST) |
458 			LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
459 			sparx5, LRN_COMMON_ACCESS_CTRL);
460 		ret = sparx5_mact_wait_for_completion(sparx5);
461 		if (ret == 0)
462 			ret = sparx5_mact_get(sparx5, mac, &vid, &cfg2);
463 		mutex_unlock(&sparx5->lock);
464 		if (ret == 0)
465 			sparx5_mact_handle_entry(sparx5, mac, vid, cfg2);
466 	} while (ret == 0);
467 
468 	mutex_lock(&sparx5->mact_lock);
469 	list_for_each_entry_safe(mact_entry, tmp, &sparx5->mact_entries,
470 				 list) {
471 		/* If the entry is in HW or permanent, then skip */
472 		if (mact_entry->flags & (MAC_ENT_ALIVE | MAC_ENT_LOCK))
473 			continue;
474 
475 		sparx5_fdb_call_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE,
476 					  mact_entry->mac, mact_entry->vid,
477 					  sparx5->ports[mact_entry->port]->ndev,
478 					  true);
479 
480 		list_del(&mact_entry->list);
481 		devm_kfree(sparx5->dev, mact_entry);
482 	}
483 	mutex_unlock(&sparx5->mact_lock);
484 
485 	queue_delayed_work(sparx5->mact_queue, &sparx5->mact_work,
486 			   SPX5_MACT_PULL_DELAY);
487 }
488 
489 void sparx5_set_ageing(struct sparx5 *sparx5, int msecs)
490 {
491 	int value = max(1, msecs / 10); /* unit 10 ms */
492 
493 	spx5_rmw(LRN_AUTOAGE_CFG_UNIT_SIZE_SET(2) | /* 10 ms */
494 		 LRN_AUTOAGE_CFG_PERIOD_VAL_SET(value / 2), /* one bit ageing */
495 		 LRN_AUTOAGE_CFG_UNIT_SIZE |
496 		 LRN_AUTOAGE_CFG_PERIOD_VAL,
497 		 sparx5,
498 		 LRN_AUTOAGE_CFG(0));
499 }
500 
501 void sparx5_mact_init(struct sparx5 *sparx5)
502 {
503 	mutex_init(&sparx5->lock);
504 
505 	/*  Flush MAC table */
506 	spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_CLEAR_ALL) |
507 		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),
508 		sparx5, LRN_COMMON_ACCESS_CTRL);
509 
510 	if (sparx5_mact_wait_for_completion(sparx5) != 0)
511 		dev_warn(sparx5->dev, "MAC flush error\n");
512 
513 	sparx5_set_ageing(sparx5, BR_DEFAULT_AGEING_TIME / HZ * 1000);
514 }
515