Linux Audio

Check our new training course

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