1 // SPDX-License-Identifier: GPL-2.0+
2 /* Microchip Sparx5 Switch driver
4 * Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.
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
];
37 #define MAC_ENT_ALIVE BIT(0)
38 #define MAC_ENT_MOVED BIT(1)
39 #define MAC_ENT_LOCK BIT(2)
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
)
53 return readx_poll_timeout(sparx5_mact_get_status
,
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
],
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.
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
;
86 if (pgid
< consts
->n_ports
) {
87 type
= MAC_ENTRY_ADDR_TYPE_UPSID_PN
;
89 addr
+= (pgid
/ 32) << 5; /* Add upsid */
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
);
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
),
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
;
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);
161 bool sparx5_mact_getnext(struct sparx5
*sparx5
,
162 unsigned char mac
[ETH_ALEN
], u16
*vid
, u32
*pcfg2
)
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
);
181 ret
= sparx5_mact_get(sparx5
, mac
, vid
, &cfg2
);
186 mutex_unlock(&sparx5
->lock
);
191 int sparx5_mact_find(struct sparx5
*sparx5
,
192 const unsigned char mac
[ETH_ALEN
], u16 vid
, u32
*pcfg2
)
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
);
208 cfg2
= spx5_rd(sparx5
, LRN_MAC_ACCESS_CFG_2
);
209 if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_GET(cfg2
))
215 mutex_unlock(&sparx5
->lock
);
220 int sparx5_mact_forget(struct sparx5
*sparx5
,
221 const unsigned char mac
[ETH_ALEN
], u16 vid
)
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
);
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
);
252 memcpy(mact_entry
->mac
, mac
, ETH_ALEN
);
253 mact_entry
->vid
= vid
;
254 mact_entry
->port
= port_index
;
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
) {
274 mutex_unlock(&sparx5
->mact_lock
);
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
= {};
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
,
294 const unsigned char *addr
, u16 vid
)
296 struct sparx5_mact_entry
*mact_entry
;
300 ret
= sparx5_mact_find(sparx5
, addr
, vid
, &cfg2
);
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
);
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
);
321 mutex_lock(&sparx5
->mact_lock
);
322 list_add_tail(&mact_entry
->list
, &sparx5
->mact_entries
);
323 mutex_unlock(&sparx5
->mact_lock
);
326 ret
= sparx5_mact_learn(sparx5
, portno
, addr
, vid
);
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
,
338 int sparx5_del_mact_entry(struct sparx5
*sparx5
,
339 const unsigned char *addr
,
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
,
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
);
363 static void sparx5_mact_handle_entry(struct sparx5
*sparx5
,
364 unsigned char mac
[ETH_ALEN
],
367 struct sparx5_mact_entry
*mact_entry
;
371 if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_TYPE_GET(cfg2
) !=
372 MAC_ENTRY_ADDR_TYPE_UPSID_PN
)
375 port
= LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_GET(cfg2
);
376 if (port
>= sparx5
->data
->consts
->n_ports
)
379 if (!test_bit(port
, sparx5
->bridge_mask
))
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
)) {
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
;
398 mutex_unlock(&sparx5
->mact_lock
);
400 if (found
&& !(mact_entry
->flags
& MAC_ENT_MOVED
))
401 /* Present, not moved */
405 /* Entry not found - now add */
406 mact_entry
= alloc_mact_entry(sparx5
, mac
, vid
, port
);
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
,
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
,
427 struct sparx5_mact_entry
*mact_entry
, *tmp
;
428 unsigned char mac
[ETH_ALEN
];
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 */
441 memset(mac
, 0, sizeof(mac
));
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
);
453 ret
= sparx5_mact_get(sparx5
, mac
, &vid
, &cfg2
);
454 mutex_unlock(&sparx5
->lock
);
456 sparx5_mact_handle_entry(sparx5
, mac
, vid
, cfg2
);
459 mutex_lock(&sparx5
->mact_lock
);
460 list_for_each_entry_safe(mact_entry
, tmp
, &sparx5
->mact_entries
,
462 /* If the entry is in HW or permanent, then skip */
463 if (mact_entry
->flags
& (MAC_ENT_ALIVE
| MAC_ENT_LOCK
))
466 sparx5_fdb_call_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE
,
467 mact_entry
->mac
, mact_entry
->vid
,
468 sparx5
->ports
[mact_entry
->port
]->ndev
,
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
,
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);