net: lan966x: More MAC table functionality
authorHoratiu Vultur <horatiu.vultur@microchip.com>
Sat, 18 Dec 2021 21:49:41 +0000 (22:49 +0100)
committerDavid S. Miller <davem@davemloft.net>
Mon, 20 Dec 2021 11:44:05 +0000 (11:44 +0000)
This patch adds support for adding/removing mac entries in the SW list
of entries and in the HW table. This is used by the bridge
functionality.

Signed-off-by: Horatiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/microchip/lan966x/lan966x_mac.c
drivers/net/ethernet/microchip/lan966x/lan966x_main.h

index 855ea514f438c25224d8db1fb14c87ad81cd695b..efadb8d326ccdc4609843948e2b4a018602c9655 100644 (file)
@@ -111,6 +111,14 @@ int lan966x_mac_cpu_forget(struct lan966x *lan966x, const char *addr, u16 vid)
        return lan966x_mac_forget(lan966x, addr, vid, ENTRYTYPE_LOCKED);
 }
 
+void lan966x_mac_set_ageing(struct lan966x *lan966x,
+                           u32 ageing)
+{
+       lan_rmw(ANA_AUTOAGE_AGE_PERIOD_SET(ageing / 2),
+               ANA_AUTOAGE_AGE_PERIOD,
+               lan966x, ANA_AUTOAGE);
+}
+
 void lan966x_mac_init(struct lan966x *lan966x)
 {
        /* Clear the MAC table */
@@ -137,6 +145,48 @@ static struct lan966x_mac_entry *lan966x_mac_alloc_entry(const unsigned char *ma
        return mac_entry;
 }
 
+static struct lan966x_mac_entry *lan966x_mac_find_entry(struct lan966x *lan966x,
+                                                       const unsigned char *mac,
+                                                       u16 vid, u16 port_index)
+{
+       struct lan966x_mac_entry *res = NULL;
+       struct lan966x_mac_entry *mac_entry;
+
+       spin_lock(&lan966x->mac_lock);
+       list_for_each_entry(mac_entry, &lan966x->mac_entries, list) {
+               if (mac_entry->vid == vid &&
+                   ether_addr_equal(mac, mac_entry->mac) &&
+                   mac_entry->port_index == port_index) {
+                       res = mac_entry;
+                       break;
+               }
+       }
+       spin_unlock(&lan966x->mac_lock);
+
+       return res;
+}
+
+static int lan966x_mac_lookup(struct lan966x *lan966x,
+                             const unsigned char mac[ETH_ALEN],
+                             unsigned int vid, enum macaccess_entry_type type)
+{
+       int ret;
+
+       lan966x_mac_select(lan966x, mac, vid);
+
+       /* Issue a read command */
+       lan_wr(ANA_MACACCESS_ENTRYTYPE_SET(type) |
+              ANA_MACACCESS_VALID_SET(1) |
+              ANA_MACACCESS_MAC_TABLE_CMD_SET(MACACCESS_CMD_READ),
+              lan966x, ANA_MACACCESS);
+
+       ret = lan966x_mac_wait_for_completion(lan966x);
+       if (ret)
+               return ret;
+
+       return ANA_MACACCESS_VALID_GET(lan_rd(lan966x, ANA_MACACCESS));
+}
+
 static void lan966x_fdb_call_notifiers(enum switchdev_notifier_type type,
                                       const char *mac, u16 vid,
                                       struct net_device *dev)
@@ -149,6 +199,61 @@ static void lan966x_fdb_call_notifiers(enum switchdev_notifier_type type,
        call_switchdev_notifiers(type, dev, &info.info, NULL);
 }
 
+int lan966x_mac_add_entry(struct lan966x *lan966x, struct lan966x_port *port,
+                         const unsigned char *addr, u16 vid)
+{
+       struct lan966x_mac_entry *mac_entry;
+
+       if (lan966x_mac_lookup(lan966x, addr, vid, ENTRYTYPE_NORMAL))
+               return 0;
+
+       /* In case the entry already exists, don't add it again to SW,
+        * just update HW, but we need to look in the actual HW because
+        * it is possible for an entry to be learn by HW and before we
+        * get the interrupt the frame will reach CPU and the CPU will
+        * add the entry but without the extern_learn flag.
+        */
+       mac_entry = lan966x_mac_find_entry(lan966x, addr, vid, port->chip_port);
+       if (mac_entry)
+               return lan966x_mac_learn(lan966x, port->chip_port,
+                                        addr, vid, ENTRYTYPE_LOCKED);
+
+       mac_entry = lan966x_mac_alloc_entry(addr, vid, port->chip_port);
+       if (!mac_entry)
+               return -ENOMEM;
+
+       spin_lock(&lan966x->mac_lock);
+       list_add_tail(&mac_entry->list, &lan966x->mac_entries);
+       spin_unlock(&lan966x->mac_lock);
+
+       lan966x_mac_learn(lan966x, port->chip_port, addr, vid, ENTRYTYPE_LOCKED);
+       lan966x_fdb_call_notifiers(SWITCHDEV_FDB_OFFLOADED, addr, vid, port->dev);
+
+       return 0;
+}
+
+int lan966x_mac_del_entry(struct lan966x *lan966x, const unsigned char *addr,
+                         u16 vid)
+{
+       struct lan966x_mac_entry *mac_entry, *tmp;
+
+       spin_lock(&lan966x->mac_lock);
+       list_for_each_entry_safe(mac_entry, tmp, &lan966x->mac_entries,
+                                list) {
+               if (mac_entry->vid == vid &&
+                   ether_addr_equal(addr, mac_entry->mac)) {
+                       lan966x_mac_forget(lan966x, mac_entry->mac, mac_entry->vid,
+                                          ENTRYTYPE_LOCKED);
+
+                       list_del(&mac_entry->list);
+                       kfree(mac_entry);
+               }
+       }
+       spin_unlock(&lan966x->mac_lock);
+
+       return 0;
+}
+
 void lan966x_mac_purge_entries(struct lan966x *lan966x)
 {
        struct lan966x_mac_entry *mac_entry, *tmp;
index ba548d65b58a5069bd7644168b44d90b7a9ef2a4..fcd5d09a070c36e9ea189cc4b28917e59275fb02 100644 (file)
@@ -145,6 +145,15 @@ int lan966x_mac_forget(struct lan966x *lan966x,
 int lan966x_mac_cpu_learn(struct lan966x *lan966x, const char *addr, u16 vid);
 int lan966x_mac_cpu_forget(struct lan966x *lan966x, const char *addr, u16 vid);
 void lan966x_mac_init(struct lan966x *lan966x);
+void lan966x_mac_set_ageing(struct lan966x *lan966x,
+                           u32 ageing);
+int lan966x_mac_del_entry(struct lan966x *lan966x,
+                         const unsigned char *addr,
+                         u16 vid);
+int lan966x_mac_add_entry(struct lan966x *lan966x,
+                         struct lan966x_port *port,
+                         const unsigned char *addr,
+                         u16 vid);
 void lan966x_mac_purge_entries(struct lan966x *lan966x);
 irqreturn_t lan966x_mac_irq_handler(struct lan966x *lan966x);