1 // SPDX-License-Identifier: GPL-2.0+
3 #include <net/switchdev.h>
5 #include "lan966x_main.h"
7 struct lan966x_pgid_entry {
14 struct lan966x_mdb_entry {
15 struct list_head list;
16 unsigned char mac[ETH_ALEN];
19 struct lan966x_pgid_entry *pgid;
23 void lan966x_mdb_init(struct lan966x *lan966x)
25 INIT_LIST_HEAD(&lan966x->mdb_entries);
26 INIT_LIST_HEAD(&lan966x->pgid_entries);
29 static void lan966x_mdb_purge_mdb_entries(struct lan966x *lan966x)
31 struct lan966x_mdb_entry *mdb_entry, *tmp;
33 list_for_each_entry_safe(mdb_entry, tmp, &lan966x->mdb_entries, list) {
34 list_del(&mdb_entry->list);
39 static void lan966x_mdb_purge_pgid_entries(struct lan966x *lan966x)
41 struct lan966x_pgid_entry *pgid_entry, *tmp;
43 list_for_each_entry_safe(pgid_entry, tmp, &lan966x->pgid_entries, list) {
44 list_del(&pgid_entry->list);
49 void lan966x_mdb_deinit(struct lan966x *lan966x)
51 lan966x_mdb_purge_mdb_entries(lan966x);
52 lan966x_mdb_purge_pgid_entries(lan966x);
55 static struct lan966x_mdb_entry *
56 lan966x_mdb_entry_get(struct lan966x *lan966x,
57 const unsigned char *mac,
60 struct lan966x_mdb_entry *mdb_entry;
62 list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
63 if (ether_addr_equal(mdb_entry->mac, mac) &&
64 mdb_entry->vid == vid)
71 static struct lan966x_mdb_entry *
72 lan966x_mdb_entry_add(struct lan966x *lan966x,
73 const struct switchdev_obj_port_mdb *mdb)
75 struct lan966x_mdb_entry *mdb_entry;
77 mdb_entry = kzalloc(sizeof(*mdb_entry), GFP_KERNEL);
79 return ERR_PTR(-ENOMEM);
81 ether_addr_copy(mdb_entry->mac, mdb->addr);
82 mdb_entry->vid = mdb->vid;
84 list_add_tail(&mdb_entry->list, &lan966x->mdb_entries);
89 static void lan966x_mdb_encode_mac(unsigned char *mac,
90 struct lan966x_mdb_entry *mdb_entry,
91 enum macaccess_entry_type type)
93 ether_addr_copy(mac, mdb_entry->mac);
95 if (type == ENTRYTYPE_MACV4) {
97 mac[1] = mdb_entry->ports >> 8;
98 mac[2] = mdb_entry->ports & 0xff;
99 } else if (type == ENTRYTYPE_MACV6) {
100 mac[0] = mdb_entry->ports >> 8;
101 mac[1] = mdb_entry->ports & 0xff;
105 static int lan966x_mdb_ip_add(struct lan966x_port *port,
106 const struct switchdev_obj_port_mdb *mdb,
107 enum macaccess_entry_type type)
109 bool cpu_port = netif_is_bridge_master(mdb->obj.orig_dev);
110 struct lan966x *lan966x = port->lan966x;
111 struct lan966x_mdb_entry *mdb_entry;
112 unsigned char mac[ETH_ALEN];
113 bool cpu_copy = false;
115 mdb_entry = lan966x_mdb_entry_get(lan966x, mdb->addr, mdb->vid);
117 mdb_entry = lan966x_mdb_entry_add(lan966x, mdb);
118 if (IS_ERR(mdb_entry))
119 return PTR_ERR(mdb_entry);
121 lan966x_mdb_encode_mac(mac, mdb_entry, type);
122 lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
126 mdb_entry->cpu_copy++;
128 mdb_entry->ports |= BIT(port->chip_port);
130 /* Copy the frame to CPU only if the CPU is in the VLAN */
131 if (lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x, mdb_entry->vid) &&
135 lan966x_mdb_encode_mac(mac, mdb_entry, type);
136 return lan966x_mac_ip_learn(lan966x, cpu_copy,
137 mac, mdb_entry->vid, type);
140 static int lan966x_mdb_ip_del(struct lan966x_port *port,
141 const struct switchdev_obj_port_mdb *mdb,
142 enum macaccess_entry_type type)
144 bool cpu_port = netif_is_bridge_master(mdb->obj.orig_dev);
145 struct lan966x *lan966x = port->lan966x;
146 struct lan966x_mdb_entry *mdb_entry;
147 unsigned char mac[ETH_ALEN];
150 mdb_entry = lan966x_mdb_entry_get(lan966x, mdb->addr, mdb->vid);
154 ports = mdb_entry->ports;
156 /* If there are still other references to the CPU port then
157 * there is no point to delete and add again the same entry
159 mdb_entry->cpu_copy--;
160 if (mdb_entry->cpu_copy)
163 ports &= ~BIT(port->chip_port);
166 lan966x_mdb_encode_mac(mac, mdb_entry, type);
167 lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
169 mdb_entry->ports = ports;
171 if (!mdb_entry->ports && !mdb_entry->cpu_copy) {
172 list_del(&mdb_entry->list);
177 lan966x_mdb_encode_mac(mac, mdb_entry, type);
178 return lan966x_mac_ip_learn(lan966x, mdb_entry->cpu_copy,
179 mac, mdb_entry->vid, type);
182 static struct lan966x_pgid_entry *
183 lan966x_pgid_entry_add(struct lan966x *lan966x, int index, u16 ports)
185 struct lan966x_pgid_entry *pgid_entry;
187 pgid_entry = kzalloc(sizeof(*pgid_entry), GFP_KERNEL);
189 return ERR_PTR(-ENOMEM);
191 pgid_entry->ports = ports;
192 pgid_entry->index = index;
193 refcount_set(&pgid_entry->refcount, 1);
195 list_add_tail(&pgid_entry->list, &lan966x->pgid_entries);
200 static struct lan966x_pgid_entry *
201 lan966x_pgid_entry_get(struct lan966x *lan966x,
202 struct lan966x_mdb_entry *mdb_entry)
204 struct lan966x_pgid_entry *pgid_entry;
207 /* Try to find an existing pgid that uses the same ports as the
210 list_for_each_entry(pgid_entry, &lan966x->pgid_entries, list) {
211 if (pgid_entry->ports == mdb_entry->ports) {
212 refcount_inc(&pgid_entry->refcount);
217 /* Try to find an empty pgid entry and allocate one in case it finds it,
218 * otherwise it means that there are no more resources
220 for (index = PGID_GP_START; index < PGID_GP_END; index++) {
223 list_for_each_entry(pgid_entry, &lan966x->pgid_entries, list) {
224 if (pgid_entry->index == index) {
231 return lan966x_pgid_entry_add(lan966x, index,
235 return ERR_PTR(-ENOSPC);
238 static void lan966x_pgid_entry_del(struct lan966x *lan966x,
239 struct lan966x_pgid_entry *pgid_entry)
241 if (!refcount_dec_and_test(&pgid_entry->refcount))
244 list_del(&pgid_entry->list);
248 static int lan966x_mdb_l2_add(struct lan966x_port *port,
249 const struct switchdev_obj_port_mdb *mdb,
250 enum macaccess_entry_type type)
252 bool cpu_port = netif_is_bridge_master(mdb->obj.orig_dev);
253 struct lan966x *lan966x = port->lan966x;
254 struct lan966x_pgid_entry *pgid_entry;
255 struct lan966x_mdb_entry *mdb_entry;
256 unsigned char mac[ETH_ALEN];
258 mdb_entry = lan966x_mdb_entry_get(lan966x, mdb->addr, mdb->vid);
260 mdb_entry = lan966x_mdb_entry_add(lan966x, mdb);
261 if (IS_ERR(mdb_entry))
262 return PTR_ERR(mdb_entry);
264 lan966x_pgid_entry_del(lan966x, mdb_entry->pgid);
265 lan966x_mdb_encode_mac(mac, mdb_entry, type);
266 lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
270 mdb_entry->ports |= BIT(CPU_PORT);
271 mdb_entry->cpu_copy++;
273 mdb_entry->ports |= BIT(port->chip_port);
276 pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
277 if (IS_ERR(pgid_entry)) {
278 list_del(&mdb_entry->list);
280 return PTR_ERR(pgid_entry);
282 mdb_entry->pgid = pgid_entry;
284 /* Copy the frame to CPU only if the CPU is in the VLAN */
285 if (!lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x, mdb_entry->vid) &&
287 mdb_entry->ports &= BIT(CPU_PORT);
289 lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
291 lan966x, ANA_PGID(pgid_entry->index));
293 return lan966x_mac_learn(lan966x, pgid_entry->index, mdb_entry->mac,
294 mdb_entry->vid, type);
297 static int lan966x_mdb_l2_del(struct lan966x_port *port,
298 const struct switchdev_obj_port_mdb *mdb,
299 enum macaccess_entry_type type)
301 bool cpu_port = netif_is_bridge_master(mdb->obj.orig_dev);
302 struct lan966x *lan966x = port->lan966x;
303 struct lan966x_pgid_entry *pgid_entry;
304 struct lan966x_mdb_entry *mdb_entry;
305 unsigned char mac[ETH_ALEN];
308 mdb_entry = lan966x_mdb_entry_get(lan966x, mdb->addr, mdb->vid);
312 ports = mdb_entry->ports;
314 /* If there are still other references to the CPU port then
315 * there is no point to delete and add again the same entry
317 mdb_entry->cpu_copy--;
318 if (mdb_entry->cpu_copy)
321 ports &= ~BIT(CPU_PORT);
323 ports &= ~BIT(port->chip_port);
326 lan966x_mdb_encode_mac(mac, mdb_entry, type);
327 lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
328 lan966x_pgid_entry_del(lan966x, mdb_entry->pgid);
330 mdb_entry->ports = ports;
332 if (!mdb_entry->ports) {
333 list_del(&mdb_entry->list);
338 pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
339 if (IS_ERR(pgid_entry)) {
340 list_del(&mdb_entry->list);
342 return PTR_ERR(pgid_entry);
344 mdb_entry->pgid = pgid_entry;
346 lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
348 lan966x, ANA_PGID(pgid_entry->index));
350 return lan966x_mac_learn(lan966x, pgid_entry->index, mdb_entry->mac,
351 mdb_entry->vid, type);
354 static enum macaccess_entry_type
355 lan966x_mdb_classify(const unsigned char *mac)
357 if (mac[0] == 0x01 && mac[1] == 0x00 && mac[2] == 0x5e)
358 return ENTRYTYPE_MACV4;
359 if (mac[0] == 0x33 && mac[1] == 0x33)
360 return ENTRYTYPE_MACV6;
361 return ENTRYTYPE_LOCKED;
364 int lan966x_handle_port_mdb_add(struct lan966x_port *port,
365 const struct switchdev_obj *obj)
367 const struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
368 enum macaccess_entry_type type;
370 /* Split the way the entries are added for ipv4/ipv6 and for l2. The
371 * reason is that for ipv4/ipv6 it doesn't require to use any pgid
372 * entry, while for l2 is required to use pgid entries
374 type = lan966x_mdb_classify(mdb->addr);
375 if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
376 return lan966x_mdb_ip_add(port, mdb, type);
378 return lan966x_mdb_l2_add(port, mdb, type);
381 int lan966x_handle_port_mdb_del(struct lan966x_port *port,
382 const struct switchdev_obj *obj)
384 const struct switchdev_obj_port_mdb *mdb = SWITCHDEV_OBJ_PORT_MDB(obj);
385 enum macaccess_entry_type type;
387 /* Split the way the entries are removed for ipv4/ipv6 and for l2. The
388 * reason is that for ipv4/ipv6 it doesn't require to use any pgid
389 * entry, while for l2 is required to use pgid entries
391 type = lan966x_mdb_classify(mdb->addr);
392 if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
393 return lan966x_mdb_ip_del(port, mdb, type);
395 return lan966x_mdb_l2_del(port, mdb, type);
398 static void lan966x_mdb_ip_cpu_copy(struct lan966x *lan966x,
399 struct lan966x_mdb_entry *mdb_entry,
400 enum macaccess_entry_type type)
402 unsigned char mac[ETH_ALEN];
404 lan966x_mdb_encode_mac(mac, mdb_entry, type);
405 lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
406 lan966x_mac_ip_learn(lan966x, true, mac, mdb_entry->vid, type);
409 static void lan966x_mdb_l2_cpu_copy(struct lan966x *lan966x,
410 struct lan966x_mdb_entry *mdb_entry,
411 enum macaccess_entry_type type)
413 struct lan966x_pgid_entry *pgid_entry;
414 unsigned char mac[ETH_ALEN];
416 lan966x_pgid_entry_del(lan966x, mdb_entry->pgid);
417 lan966x_mdb_encode_mac(mac, mdb_entry, type);
418 lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
420 mdb_entry->ports |= BIT(CPU_PORT);
422 pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
423 if (IS_ERR(pgid_entry))
426 mdb_entry->pgid = pgid_entry;
428 lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
430 lan966x, ANA_PGID(pgid_entry->index));
432 lan966x_mac_learn(lan966x, pgid_entry->index, mdb_entry->mac,
433 mdb_entry->vid, type);
436 void lan966x_mdb_write_entries(struct lan966x *lan966x, u16 vid)
438 struct lan966x_mdb_entry *mdb_entry;
439 enum macaccess_entry_type type;
441 list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
442 if (mdb_entry->vid != vid || !mdb_entry->cpu_copy)
445 type = lan966x_mdb_classify(mdb_entry->mac);
446 if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
447 lan966x_mdb_ip_cpu_copy(lan966x, mdb_entry, type);
449 lan966x_mdb_l2_cpu_copy(lan966x, mdb_entry, type);
453 static void lan966x_mdb_ip_cpu_remove(struct lan966x *lan966x,
454 struct lan966x_mdb_entry *mdb_entry,
455 enum macaccess_entry_type type)
457 unsigned char mac[ETH_ALEN];
459 lan966x_mdb_encode_mac(mac, mdb_entry, type);
460 lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
461 lan966x_mac_ip_learn(lan966x, false, mac, mdb_entry->vid, type);
464 static void lan966x_mdb_l2_cpu_remove(struct lan966x *lan966x,
465 struct lan966x_mdb_entry *mdb_entry,
466 enum macaccess_entry_type type)
468 struct lan966x_pgid_entry *pgid_entry;
469 unsigned char mac[ETH_ALEN];
471 lan966x_pgid_entry_del(lan966x, mdb_entry->pgid);
472 lan966x_mdb_encode_mac(mac, mdb_entry, type);
473 lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
475 mdb_entry->ports &= ~BIT(CPU_PORT);
477 pgid_entry = lan966x_pgid_entry_get(lan966x, mdb_entry);
478 if (IS_ERR(pgid_entry))
481 mdb_entry->pgid = pgid_entry;
483 lan_rmw(ANA_PGID_PGID_SET(mdb_entry->ports),
485 lan966x, ANA_PGID(pgid_entry->index));
487 lan966x_mac_learn(lan966x, pgid_entry->index, mdb_entry->mac,
488 mdb_entry->vid, type);
491 void lan966x_mdb_erase_entries(struct lan966x *lan966x, u16 vid)
493 struct lan966x_mdb_entry *mdb_entry;
494 enum macaccess_entry_type type;
496 list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
497 if (mdb_entry->vid != vid || !mdb_entry->cpu_copy)
500 type = lan966x_mdb_classify(mdb_entry->mac);
501 if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6)
502 lan966x_mdb_ip_cpu_remove(lan966x, mdb_entry, type);
504 lan966x_mdb_l2_cpu_remove(lan966x, mdb_entry, type);
508 void lan966x_mdb_clear_entries(struct lan966x *lan966x)
510 struct lan966x_mdb_entry *mdb_entry;
511 enum macaccess_entry_type type;
512 unsigned char mac[ETH_ALEN];
514 list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
515 type = lan966x_mdb_classify(mdb_entry->mac);
517 lan966x_mdb_encode_mac(mac, mdb_entry, type);
518 /* Remove just the MAC entry, still keep the PGID in case of L2
519 * entries because this can be restored at later point
521 lan966x_mac_forget(lan966x, mac, mdb_entry->vid, type);
525 void lan966x_mdb_restore_entries(struct lan966x *lan966x)
527 struct lan966x_mdb_entry *mdb_entry;
528 enum macaccess_entry_type type;
529 unsigned char mac[ETH_ALEN];
530 bool cpu_copy = false;
532 list_for_each_entry(mdb_entry, &lan966x->mdb_entries, list) {
533 type = lan966x_mdb_classify(mdb_entry->mac);
535 lan966x_mdb_encode_mac(mac, mdb_entry, type);
536 if (type == ENTRYTYPE_MACV4 || type == ENTRYTYPE_MACV6) {
537 /* Copy the frame to CPU only if the CPU is in the VLAN */
538 if (lan966x_vlan_cpu_member_cpu_vlan_mask(lan966x,
543 lan966x_mac_ip_learn(lan966x, cpu_copy, mac,
544 mdb_entry->vid, type);
546 lan966x_mac_learn(lan966x, mdb_entry->pgid->index,
548 mdb_entry->vid, type);