Merge tag 'trace-printf-v6.13' of git://git.kernel.org/pub/scm/linux/kernel/git/trace...
[drm/drm-misc.git] / drivers / net / ethernet / microchip / sparx5 / sparx5_mactable.c
blobf5584244612cd9b8164b71103e4087a94226b1eb
1 // SPDX-License-Identifier: GPL-2.0+
2 /* Microchip Sparx5 Switch driver
4 * Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.
5 */
7 #include <net/switchdev.h>
8 #include <linux/if_bridge.h>
9 #include <linux/iopoll.h>
11 #include "sparx5_main_regs.h"
12 #include "sparx5_main.h"
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 */
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
30 #define TABLE_UPDATE_SLEEP_US 10
31 #define TABLE_UPDATE_TIMEOUT_US 100000
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;
44 static int sparx5_mact_get_status(struct sparx5 *sparx5)
46 return spx5_rd(sparx5, LRN_COMMON_ACCESS_CTRL);
49 static int sparx5_mact_wait_for_completion(struct sparx5 *sparx5)
51 u32 val;
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);
59 static void sparx5_mact_select(struct sparx5 *sparx5,
60 const unsigned char mac[ETH_ALEN],
61 u16 vid)
63 u32 macl = 0, mach = 0;
65 /* Set the MAC address to handle and the vlan associated in a format
66 * understood by the hardware.
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;
76 spx5_wr(mach, sparx5, LRN_MAC_ACCESS_CFG_0);
77 spx5_wr(macl, sparx5, LRN_MAC_ACCESS_CFG_1);
80 int sparx5_mact_learn(struct sparx5 *sparx5, int pgid,
81 const unsigned char mac[ETH_ALEN], u16 vid)
83 const struct sparx5_consts *consts = sparx5->data->consts;
84 int addr, type, ret;
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;
95 mutex_lock(&sparx5->lock);
97 sparx5_mact_select(sparx5, mac, vid);
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);
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);
112 ret = sparx5_mact_wait_for_completion(sparx5);
114 mutex_unlock(&sparx5->lock);
116 return ret;
119 int sparx5_mc_unsync(struct net_device *dev, const unsigned char *addr)
121 struct sparx5_port *port = netdev_priv(dev);
122 struct sparx5 *sparx5 = port->sparx5;
124 return sparx5_mact_forget(sparx5, addr, port->pvid);
127 int sparx5_mc_sync(struct net_device *dev, const unsigned char *addr)
129 struct sparx5_port *port = netdev_priv(dev);
130 struct sparx5 *sparx5 = port->sparx5;
132 return sparx5_mact_learn(sparx5, sparx5_get_pgid(sparx5, PGID_CPU),
133 addr, port->pvid);
136 static int sparx5_mact_get(struct sparx5 *sparx5,
137 unsigned char mac[ETH_ALEN],
138 u16 *vid, u32 *pcfg2)
140 u32 mach, macl, cfg2;
141 int ret = -ENOENT;
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;
158 return ret;
161 bool sparx5_mact_getnext(struct sparx5 *sparx5,
162 unsigned char mac[ETH_ALEN], u16 *vid, u32 *pcfg2)
164 u32 cfg2;
165 int ret;
167 mutex_lock(&sparx5->lock);
169 sparx5_mact_select(sparx5, mac, *vid);
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);
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;
186 mutex_unlock(&sparx5->lock);
188 return ret == 0;
191 int sparx5_mact_find(struct sparx5 *sparx5,
192 const unsigned char mac[ETH_ALEN], u16 vid, u32 *pcfg2)
194 int ret;
195 u32 cfg2;
197 mutex_lock(&sparx5->lock);
199 sparx5_mact_select(sparx5, mac, vid);
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);
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;
215 mutex_unlock(&sparx5->lock);
217 return ret;
220 int sparx5_mact_forget(struct sparx5 *sparx5,
221 const unsigned char mac[ETH_ALEN], u16 vid)
223 int ret;
225 mutex_lock(&sparx5->lock);
227 sparx5_mact_select(sparx5, mac, vid);
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);
234 ret = sparx5_mact_wait_for_completion(sparx5);
236 mutex_unlock(&sparx5->lock);
238 return ret;
241 static struct sparx5_mact_entry *alloc_mact_entry(struct sparx5 *sparx5,
242 const unsigned char *mac,
243 u16 vid, u16 port_index)
245 struct sparx5_mact_entry *mact_entry;
247 mact_entry = devm_kzalloc(sparx5->dev,
248 sizeof(*mact_entry), GFP_ATOMIC);
249 if (!mact_entry)
250 return NULL;
252 memcpy(mact_entry->mac, mac, ETH_ALEN);
253 mact_entry->vid = vid;
254 mact_entry->port = port_index;
255 return mact_entry;
258 static struct sparx5_mact_entry *find_mact_entry(struct sparx5 *sparx5,
259 const unsigned char *mac,
260 u16 vid, u16 port_index)
262 struct sparx5_mact_entry *mact_entry;
263 struct sparx5_mact_entry *res = NULL;
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;
274 mutex_unlock(&sparx5->mact_lock);
276 return res;
279 static void sparx5_fdb_call_notifiers(enum switchdev_notifier_type type,
280 const char *mac, u16 vid,
281 struct net_device *dev, bool offloaded)
283 struct switchdev_notifier_fdb_info info = {};
285 info.addr = mac;
286 info.vid = vid;
287 info.offloaded = offloaded;
288 call_switchdev_notifiers(type, dev, &info.info, NULL);
291 int sparx5_add_mact_entry(struct sparx5 *sparx5,
292 struct net_device *dev,
293 u16 portno,
294 const unsigned char *addr, u16 vid)
296 struct sparx5_mact_entry *mact_entry;
297 int ret;
298 u32 cfg2;
300 ret = sparx5_mact_find(sparx5, addr, vid, &cfg2);
301 if (!ret)
302 return 0;
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.
310 mact_entry = find_mact_entry(sparx5, addr, vid, portno);
311 if (mact_entry)
312 goto update_hw;
314 /* Add the entry in SW MAC table not to get the notification when
315 * SW is pulling again
317 mact_entry = alloc_mact_entry(sparx5, addr, vid, portno);
318 if (!mact_entry)
319 return -ENOMEM;
321 mutex_lock(&sparx5->mact_lock);
322 list_add_tail(&mact_entry->list, &sparx5->mact_entries);
323 mutex_unlock(&sparx5->mact_lock);
325 update_hw:
326 ret = sparx5_mact_learn(sparx5, portno, addr, vid);
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);
335 return ret;
338 int sparx5_del_mact_entry(struct sparx5 *sparx5,
339 const unsigned char *addr,
340 u16 vid)
342 struct sparx5_mact_entry *mact_entry, *tmp;
344 /* Delete the entry in SW MAC table not to get the notification when
345 * SW is pulling again
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);
354 list_del(&mact_entry->list);
355 devm_kfree(sparx5->dev, mact_entry);
358 mutex_unlock(&sparx5->mact_lock);
360 return 0;
363 static void sparx5_mact_handle_entry(struct sparx5 *sparx5,
364 unsigned char mac[ETH_ALEN],
365 u16 vid, u32 cfg2)
367 struct sparx5_mact_entry *mact_entry;
368 bool found = false;
369 u16 port;
371 if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_TYPE_GET(cfg2) !=
372 MAC_ENTRY_ADDR_TYPE_UPSID_PN)
373 return;
375 port = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_GET(cfg2);
376 if (port >= sparx5->data->consts->n_ports)
377 return;
379 if (!test_bit(port, sparx5->bridge_mask))
380 return;
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;
394 /* Entry handled */
395 break;
398 mutex_unlock(&sparx5->mact_lock);
400 if (found && !(mact_entry->flags & MAC_ENT_MOVED))
401 /* Present, not moved */
402 return;
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;
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);
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);
422 void sparx5_mact_pull_work(struct work_struct *work)
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;
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);
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);
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;
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);
471 list_del(&mact_entry->list);
472 devm_kfree(sparx5->dev, mact_entry);
474 mutex_unlock(&sparx5->mact_lock);
476 queue_delayed_work(sparx5->mact_queue, &sparx5->mact_work,
477 SPX5_MACT_PULL_DELAY);
480 void sparx5_set_ageing(struct sparx5 *sparx5, int msecs)
482 int value = max(1, msecs / 10); /* unit 10 ms */
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));
492 void sparx5_mact_init(struct sparx5 *sparx5)
494 mutex_init(&sparx5->lock);
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);
501 if (sparx5_mact_wait_for_completion(sparx5) != 0)
502 dev_warn(sparx5->dev, "MAC flush error\n");
504 sparx5_set_ageing(sparx5, BR_DEFAULT_AGEING_TIME / HZ * 1000);