162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 262306a36Sopenharmony_ci/* Microchip Sparx5 Switch driver 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#include <net/switchdev.h> 862306a36Sopenharmony_ci#include <linux/if_bridge.h> 962306a36Sopenharmony_ci#include <linux/iopoll.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include "sparx5_main_regs.h" 1262306a36Sopenharmony_ci#include "sparx5_main.h" 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci/* Commands for Mac Table Command register */ 1562306a36Sopenharmony_ci#define MAC_CMD_LEARN 0 /* Insert (Learn) 1 entry */ 1662306a36Sopenharmony_ci#define MAC_CMD_UNLEARN 1 /* Unlearn (Forget) 1 entry */ 1762306a36Sopenharmony_ci#define MAC_CMD_LOOKUP 2 /* Look up 1 entry */ 1862306a36Sopenharmony_ci#define MAC_CMD_READ 3 /* Read entry at Mac Table Index */ 1962306a36Sopenharmony_ci#define MAC_CMD_WRITE 4 /* Write entry at Mac Table Index */ 2062306a36Sopenharmony_ci#define MAC_CMD_SCAN 5 /* Scan (Age or find next) */ 2162306a36Sopenharmony_ci#define MAC_CMD_FIND_SMALLEST 6 /* Get next entry */ 2262306a36Sopenharmony_ci#define MAC_CMD_CLEAR_ALL 7 /* Delete all entries in table */ 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci/* Commands for MAC_ENTRY_ADDR_TYPE */ 2562306a36Sopenharmony_ci#define MAC_ENTRY_ADDR_TYPE_UPSID_PN 0 2662306a36Sopenharmony_ci#define MAC_ENTRY_ADDR_TYPE_UPSID_CPU_OR_INT 1 2762306a36Sopenharmony_ci#define MAC_ENTRY_ADDR_TYPE_GLAG 2 2862306a36Sopenharmony_ci#define MAC_ENTRY_ADDR_TYPE_MC_IDX 3 2962306a36Sopenharmony_ci 3062306a36Sopenharmony_ci#define TABLE_UPDATE_SLEEP_US 10 3162306a36Sopenharmony_ci#define TABLE_UPDATE_TIMEOUT_US 100000 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistruct sparx5_mact_entry { 3462306a36Sopenharmony_ci struct list_head list; 3562306a36Sopenharmony_ci unsigned char mac[ETH_ALEN]; 3662306a36Sopenharmony_ci u32 flags; 3762306a36Sopenharmony_ci#define MAC_ENT_ALIVE BIT(0) 3862306a36Sopenharmony_ci#define MAC_ENT_MOVED BIT(1) 3962306a36Sopenharmony_ci#define MAC_ENT_LOCK BIT(2) 4062306a36Sopenharmony_ci u16 vid; 4162306a36Sopenharmony_ci u16 port; 4262306a36Sopenharmony_ci}; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_cistatic int sparx5_mact_get_status(struct sparx5 *sparx5) 4562306a36Sopenharmony_ci{ 4662306a36Sopenharmony_ci return spx5_rd(sparx5, LRN_COMMON_ACCESS_CTRL); 4762306a36Sopenharmony_ci} 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_cistatic int sparx5_mact_wait_for_completion(struct sparx5 *sparx5) 5062306a36Sopenharmony_ci{ 5162306a36Sopenharmony_ci u32 val; 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci return readx_poll_timeout(sparx5_mact_get_status, 5462306a36Sopenharmony_ci sparx5, val, 5562306a36Sopenharmony_ci LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_GET(val) == 0, 5662306a36Sopenharmony_ci TABLE_UPDATE_SLEEP_US, TABLE_UPDATE_TIMEOUT_US); 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_cistatic void sparx5_mact_select(struct sparx5 *sparx5, 6062306a36Sopenharmony_ci const unsigned char mac[ETH_ALEN], 6162306a36Sopenharmony_ci u16 vid) 6262306a36Sopenharmony_ci{ 6362306a36Sopenharmony_ci u32 macl = 0, mach = 0; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci /* Set the MAC address to handle and the vlan associated in a format 6662306a36Sopenharmony_ci * understood by the hardware. 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_ci mach |= vid << 16; 6962306a36Sopenharmony_ci mach |= mac[0] << 8; 7062306a36Sopenharmony_ci mach |= mac[1] << 0; 7162306a36Sopenharmony_ci macl |= mac[2] << 24; 7262306a36Sopenharmony_ci macl |= mac[3] << 16; 7362306a36Sopenharmony_ci macl |= mac[4] << 8; 7462306a36Sopenharmony_ci macl |= mac[5] << 0; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci spx5_wr(mach, sparx5, LRN_MAC_ACCESS_CFG_0); 7762306a36Sopenharmony_ci spx5_wr(macl, sparx5, LRN_MAC_ACCESS_CFG_1); 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ciint sparx5_mact_learn(struct sparx5 *sparx5, int pgid, 8162306a36Sopenharmony_ci const unsigned char mac[ETH_ALEN], u16 vid) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci int addr, type, ret; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci if (pgid < SPX5_PORTS) { 8662306a36Sopenharmony_ci type = MAC_ENTRY_ADDR_TYPE_UPSID_PN; 8762306a36Sopenharmony_ci addr = pgid % 32; 8862306a36Sopenharmony_ci addr += (pgid / 32) << 5; /* Add upsid */ 8962306a36Sopenharmony_ci } else { 9062306a36Sopenharmony_ci type = MAC_ENTRY_ADDR_TYPE_MC_IDX; 9162306a36Sopenharmony_ci addr = pgid - SPX5_PORTS; 9262306a36Sopenharmony_ci } 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci mutex_lock(&sparx5->lock); 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci sparx5_mact_select(sparx5, mac, vid); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci /* MAC entry properties */ 9962306a36Sopenharmony_ci spx5_wr(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_SET(addr) | 10062306a36Sopenharmony_ci LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_TYPE_SET(type) | 10162306a36Sopenharmony_ci LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_SET(1) | 10262306a36Sopenharmony_ci LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_LOCKED_SET(1), 10362306a36Sopenharmony_ci sparx5, LRN_MAC_ACCESS_CFG_2); 10462306a36Sopenharmony_ci spx5_wr(0, sparx5, LRN_MAC_ACCESS_CFG_3); 10562306a36Sopenharmony_ci 10662306a36Sopenharmony_ci /* Insert/learn new entry */ 10762306a36Sopenharmony_ci spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_LEARN) | 10862306a36Sopenharmony_ci LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1), 10962306a36Sopenharmony_ci sparx5, LRN_COMMON_ACCESS_CTRL); 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci ret = sparx5_mact_wait_for_completion(sparx5); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci mutex_unlock(&sparx5->lock); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci return ret; 11662306a36Sopenharmony_ci} 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ciint sparx5_mc_unsync(struct net_device *dev, const unsigned char *addr) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct sparx5_port *port = netdev_priv(dev); 12162306a36Sopenharmony_ci struct sparx5 *sparx5 = port->sparx5; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci return sparx5_mact_forget(sparx5, addr, port->pvid); 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ciint sparx5_mc_sync(struct net_device *dev, const unsigned char *addr) 12762306a36Sopenharmony_ci{ 12862306a36Sopenharmony_ci struct sparx5_port *port = netdev_priv(dev); 12962306a36Sopenharmony_ci struct sparx5 *sparx5 = port->sparx5; 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci return sparx5_mact_learn(sparx5, PGID_CPU, addr, port->pvid); 13262306a36Sopenharmony_ci} 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic int sparx5_mact_get(struct sparx5 *sparx5, 13562306a36Sopenharmony_ci unsigned char mac[ETH_ALEN], 13662306a36Sopenharmony_ci u16 *vid, u32 *pcfg2) 13762306a36Sopenharmony_ci{ 13862306a36Sopenharmony_ci u32 mach, macl, cfg2; 13962306a36Sopenharmony_ci int ret = -ENOENT; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci cfg2 = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_2); 14262306a36Sopenharmony_ci if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_GET(cfg2)) { 14362306a36Sopenharmony_ci mach = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_0); 14462306a36Sopenharmony_ci macl = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_1); 14562306a36Sopenharmony_ci mac[0] = ((mach >> 8) & 0xff); 14662306a36Sopenharmony_ci mac[1] = ((mach >> 0) & 0xff); 14762306a36Sopenharmony_ci mac[2] = ((macl >> 24) & 0xff); 14862306a36Sopenharmony_ci mac[3] = ((macl >> 16) & 0xff); 14962306a36Sopenharmony_ci mac[4] = ((macl >> 8) & 0xff); 15062306a36Sopenharmony_ci mac[5] = ((macl >> 0) & 0xff); 15162306a36Sopenharmony_ci *vid = mach >> 16; 15262306a36Sopenharmony_ci *pcfg2 = cfg2; 15362306a36Sopenharmony_ci ret = 0; 15462306a36Sopenharmony_ci } 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci return ret; 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_cibool sparx5_mact_getnext(struct sparx5 *sparx5, 16062306a36Sopenharmony_ci unsigned char mac[ETH_ALEN], u16 *vid, u32 *pcfg2) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci u32 cfg2; 16362306a36Sopenharmony_ci int ret; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci mutex_lock(&sparx5->lock); 16662306a36Sopenharmony_ci 16762306a36Sopenharmony_ci sparx5_mact_select(sparx5, mac, *vid); 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci spx5_wr(LRN_SCAN_NEXT_CFG_SCAN_NEXT_IGNORE_LOCKED_ENA_SET(1) | 17062306a36Sopenharmony_ci LRN_SCAN_NEXT_CFG_SCAN_NEXT_UNTIL_FOUND_ENA_SET(1), 17162306a36Sopenharmony_ci sparx5, LRN_SCAN_NEXT_CFG); 17262306a36Sopenharmony_ci spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET 17362306a36Sopenharmony_ci (MAC_CMD_FIND_SMALLEST) | 17462306a36Sopenharmony_ci LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1), 17562306a36Sopenharmony_ci sparx5, LRN_COMMON_ACCESS_CTRL); 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_ci ret = sparx5_mact_wait_for_completion(sparx5); 17862306a36Sopenharmony_ci if (ret == 0) { 17962306a36Sopenharmony_ci ret = sparx5_mact_get(sparx5, mac, vid, &cfg2); 18062306a36Sopenharmony_ci if (ret == 0) 18162306a36Sopenharmony_ci *pcfg2 = cfg2; 18262306a36Sopenharmony_ci } 18362306a36Sopenharmony_ci 18462306a36Sopenharmony_ci mutex_unlock(&sparx5->lock); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci return ret == 0; 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ciint sparx5_mact_find(struct sparx5 *sparx5, 19062306a36Sopenharmony_ci const unsigned char mac[ETH_ALEN], u16 vid, u32 *pcfg2) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci int ret; 19362306a36Sopenharmony_ci u32 cfg2; 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_ci mutex_lock(&sparx5->lock); 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci sparx5_mact_select(sparx5, mac, vid); 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_ci /* Issue a lookup command */ 20062306a36Sopenharmony_ci spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_LOOKUP) | 20162306a36Sopenharmony_ci LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1), 20262306a36Sopenharmony_ci sparx5, LRN_COMMON_ACCESS_CTRL); 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_ci ret = sparx5_mact_wait_for_completion(sparx5); 20562306a36Sopenharmony_ci if (ret == 0) { 20662306a36Sopenharmony_ci cfg2 = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_2); 20762306a36Sopenharmony_ci if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_GET(cfg2)) 20862306a36Sopenharmony_ci *pcfg2 = cfg2; 20962306a36Sopenharmony_ci else 21062306a36Sopenharmony_ci ret = -ENOENT; 21162306a36Sopenharmony_ci } 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci mutex_unlock(&sparx5->lock); 21462306a36Sopenharmony_ci 21562306a36Sopenharmony_ci return ret; 21662306a36Sopenharmony_ci} 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ciint sparx5_mact_forget(struct sparx5 *sparx5, 21962306a36Sopenharmony_ci const unsigned char mac[ETH_ALEN], u16 vid) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci int ret; 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_ci mutex_lock(&sparx5->lock); 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci sparx5_mact_select(sparx5, mac, vid); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci /* Issue an unlearn command */ 22862306a36Sopenharmony_ci spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_UNLEARN) | 22962306a36Sopenharmony_ci LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1), 23062306a36Sopenharmony_ci sparx5, LRN_COMMON_ACCESS_CTRL); 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci ret = sparx5_mact_wait_for_completion(sparx5); 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci mutex_unlock(&sparx5->lock); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci return ret; 23762306a36Sopenharmony_ci} 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_cistatic struct sparx5_mact_entry *alloc_mact_entry(struct sparx5 *sparx5, 24062306a36Sopenharmony_ci const unsigned char *mac, 24162306a36Sopenharmony_ci u16 vid, u16 port_index) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci struct sparx5_mact_entry *mact_entry; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci mact_entry = devm_kzalloc(sparx5->dev, 24662306a36Sopenharmony_ci sizeof(*mact_entry), GFP_ATOMIC); 24762306a36Sopenharmony_ci if (!mact_entry) 24862306a36Sopenharmony_ci return NULL; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci memcpy(mact_entry->mac, mac, ETH_ALEN); 25162306a36Sopenharmony_ci mact_entry->vid = vid; 25262306a36Sopenharmony_ci mact_entry->port = port_index; 25362306a36Sopenharmony_ci return mact_entry; 25462306a36Sopenharmony_ci} 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_cistatic struct sparx5_mact_entry *find_mact_entry(struct sparx5 *sparx5, 25762306a36Sopenharmony_ci const unsigned char *mac, 25862306a36Sopenharmony_ci u16 vid, u16 port_index) 25962306a36Sopenharmony_ci{ 26062306a36Sopenharmony_ci struct sparx5_mact_entry *mact_entry; 26162306a36Sopenharmony_ci struct sparx5_mact_entry *res = NULL; 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci mutex_lock(&sparx5->mact_lock); 26462306a36Sopenharmony_ci list_for_each_entry(mact_entry, &sparx5->mact_entries, list) { 26562306a36Sopenharmony_ci if (mact_entry->vid == vid && 26662306a36Sopenharmony_ci ether_addr_equal(mac, mact_entry->mac) && 26762306a36Sopenharmony_ci mact_entry->port == port_index) { 26862306a36Sopenharmony_ci res = mact_entry; 26962306a36Sopenharmony_ci break; 27062306a36Sopenharmony_ci } 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci mutex_unlock(&sparx5->mact_lock); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci return res; 27562306a36Sopenharmony_ci} 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_cistatic void sparx5_fdb_call_notifiers(enum switchdev_notifier_type type, 27862306a36Sopenharmony_ci const char *mac, u16 vid, 27962306a36Sopenharmony_ci struct net_device *dev, bool offloaded) 28062306a36Sopenharmony_ci{ 28162306a36Sopenharmony_ci struct switchdev_notifier_fdb_info info = {}; 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci info.addr = mac; 28462306a36Sopenharmony_ci info.vid = vid; 28562306a36Sopenharmony_ci info.offloaded = offloaded; 28662306a36Sopenharmony_ci call_switchdev_notifiers(type, dev, &info.info, NULL); 28762306a36Sopenharmony_ci} 28862306a36Sopenharmony_ci 28962306a36Sopenharmony_ciint sparx5_add_mact_entry(struct sparx5 *sparx5, 29062306a36Sopenharmony_ci struct net_device *dev, 29162306a36Sopenharmony_ci u16 portno, 29262306a36Sopenharmony_ci const unsigned char *addr, u16 vid) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci struct sparx5_mact_entry *mact_entry; 29562306a36Sopenharmony_ci int ret; 29662306a36Sopenharmony_ci u32 cfg2; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci ret = sparx5_mact_find(sparx5, addr, vid, &cfg2); 29962306a36Sopenharmony_ci if (!ret) 30062306a36Sopenharmony_ci return 0; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci /* In case the entry already exists, don't add it again to SW, 30362306a36Sopenharmony_ci * just update HW, but we need to look in the actual HW because 30462306a36Sopenharmony_ci * it is possible for an entry to be learn by HW and before the 30562306a36Sopenharmony_ci * mact thread to start the frame will reach CPU and the CPU will 30662306a36Sopenharmony_ci * add the entry but without the extern_learn flag. 30762306a36Sopenharmony_ci */ 30862306a36Sopenharmony_ci mact_entry = find_mact_entry(sparx5, addr, vid, portno); 30962306a36Sopenharmony_ci if (mact_entry) 31062306a36Sopenharmony_ci goto update_hw; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci /* Add the entry in SW MAC table not to get the notification when 31362306a36Sopenharmony_ci * SW is pulling again 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_ci mact_entry = alloc_mact_entry(sparx5, addr, vid, portno); 31662306a36Sopenharmony_ci if (!mact_entry) 31762306a36Sopenharmony_ci return -ENOMEM; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci mutex_lock(&sparx5->mact_lock); 32062306a36Sopenharmony_ci list_add_tail(&mact_entry->list, &sparx5->mact_entries); 32162306a36Sopenharmony_ci mutex_unlock(&sparx5->mact_lock); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ciupdate_hw: 32462306a36Sopenharmony_ci ret = sparx5_mact_learn(sparx5, portno, addr, vid); 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci /* New entry? */ 32762306a36Sopenharmony_ci if (mact_entry->flags == 0) { 32862306a36Sopenharmony_ci mact_entry->flags |= MAC_ENT_LOCK; /* Don't age this */ 32962306a36Sopenharmony_ci sparx5_fdb_call_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, addr, vid, 33062306a36Sopenharmony_ci dev, true); 33162306a36Sopenharmony_ci } 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci return ret; 33462306a36Sopenharmony_ci} 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ciint sparx5_del_mact_entry(struct sparx5 *sparx5, 33762306a36Sopenharmony_ci const unsigned char *addr, 33862306a36Sopenharmony_ci u16 vid) 33962306a36Sopenharmony_ci{ 34062306a36Sopenharmony_ci struct sparx5_mact_entry *mact_entry, *tmp; 34162306a36Sopenharmony_ci 34262306a36Sopenharmony_ci /* Delete the entry in SW MAC table not to get the notification when 34362306a36Sopenharmony_ci * SW is pulling again 34462306a36Sopenharmony_ci */ 34562306a36Sopenharmony_ci mutex_lock(&sparx5->mact_lock); 34662306a36Sopenharmony_ci list_for_each_entry_safe(mact_entry, tmp, &sparx5->mact_entries, 34762306a36Sopenharmony_ci list) { 34862306a36Sopenharmony_ci if ((vid == 0 || mact_entry->vid == vid) && 34962306a36Sopenharmony_ci ether_addr_equal(addr, mact_entry->mac)) { 35062306a36Sopenharmony_ci sparx5_mact_forget(sparx5, addr, mact_entry->vid); 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci list_del(&mact_entry->list); 35362306a36Sopenharmony_ci devm_kfree(sparx5->dev, mact_entry); 35462306a36Sopenharmony_ci } 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci mutex_unlock(&sparx5->mact_lock); 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_ci return 0; 35962306a36Sopenharmony_ci} 36062306a36Sopenharmony_ci 36162306a36Sopenharmony_cistatic void sparx5_mact_handle_entry(struct sparx5 *sparx5, 36262306a36Sopenharmony_ci unsigned char mac[ETH_ALEN], 36362306a36Sopenharmony_ci u16 vid, u32 cfg2) 36462306a36Sopenharmony_ci{ 36562306a36Sopenharmony_ci struct sparx5_mact_entry *mact_entry; 36662306a36Sopenharmony_ci bool found = false; 36762306a36Sopenharmony_ci u16 port; 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_TYPE_GET(cfg2) != 37062306a36Sopenharmony_ci MAC_ENTRY_ADDR_TYPE_UPSID_PN) 37162306a36Sopenharmony_ci return; 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci port = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_GET(cfg2); 37462306a36Sopenharmony_ci if (port >= SPX5_PORTS) 37562306a36Sopenharmony_ci return; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci if (!test_bit(port, sparx5->bridge_mask)) 37862306a36Sopenharmony_ci return; 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci mutex_lock(&sparx5->mact_lock); 38162306a36Sopenharmony_ci list_for_each_entry(mact_entry, &sparx5->mact_entries, list) { 38262306a36Sopenharmony_ci if (mact_entry->vid == vid && 38362306a36Sopenharmony_ci ether_addr_equal(mac, mact_entry->mac)) { 38462306a36Sopenharmony_ci found = true; 38562306a36Sopenharmony_ci mact_entry->flags |= MAC_ENT_ALIVE; 38662306a36Sopenharmony_ci if (mact_entry->port != port) { 38762306a36Sopenharmony_ci dev_warn(sparx5->dev, "Entry move: %d -> %d\n", 38862306a36Sopenharmony_ci mact_entry->port, port); 38962306a36Sopenharmony_ci mact_entry->port = port; 39062306a36Sopenharmony_ci mact_entry->flags |= MAC_ENT_MOVED; 39162306a36Sopenharmony_ci } 39262306a36Sopenharmony_ci /* Entry handled */ 39362306a36Sopenharmony_ci break; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci } 39662306a36Sopenharmony_ci mutex_unlock(&sparx5->mact_lock); 39762306a36Sopenharmony_ci 39862306a36Sopenharmony_ci if (found && !(mact_entry->flags & MAC_ENT_MOVED)) 39962306a36Sopenharmony_ci /* Present, not moved */ 40062306a36Sopenharmony_ci return; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (!found) { 40362306a36Sopenharmony_ci /* Entry not found - now add */ 40462306a36Sopenharmony_ci mact_entry = alloc_mact_entry(sparx5, mac, vid, port); 40562306a36Sopenharmony_ci if (!mact_entry) 40662306a36Sopenharmony_ci return; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci mact_entry->flags |= MAC_ENT_ALIVE; 40962306a36Sopenharmony_ci mutex_lock(&sparx5->mact_lock); 41062306a36Sopenharmony_ci list_add_tail(&mact_entry->list, &sparx5->mact_entries); 41162306a36Sopenharmony_ci mutex_unlock(&sparx5->mact_lock); 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci /* New or moved entry - notify bridge */ 41562306a36Sopenharmony_ci sparx5_fdb_call_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, 41662306a36Sopenharmony_ci mac, vid, sparx5->ports[port]->ndev, 41762306a36Sopenharmony_ci true); 41862306a36Sopenharmony_ci} 41962306a36Sopenharmony_ci 42062306a36Sopenharmony_civoid sparx5_mact_pull_work(struct work_struct *work) 42162306a36Sopenharmony_ci{ 42262306a36Sopenharmony_ci struct delayed_work *del_work = to_delayed_work(work); 42362306a36Sopenharmony_ci struct sparx5 *sparx5 = container_of(del_work, struct sparx5, 42462306a36Sopenharmony_ci mact_work); 42562306a36Sopenharmony_ci struct sparx5_mact_entry *mact_entry, *tmp; 42662306a36Sopenharmony_ci unsigned char mac[ETH_ALEN]; 42762306a36Sopenharmony_ci u32 cfg2; 42862306a36Sopenharmony_ci u16 vid; 42962306a36Sopenharmony_ci int ret; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci /* Reset MAC entry flags */ 43262306a36Sopenharmony_ci mutex_lock(&sparx5->mact_lock); 43362306a36Sopenharmony_ci list_for_each_entry(mact_entry, &sparx5->mact_entries, list) 43462306a36Sopenharmony_ci mact_entry->flags &= MAC_ENT_LOCK; 43562306a36Sopenharmony_ci mutex_unlock(&sparx5->mact_lock); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci /* MAIN mac address processing loop */ 43862306a36Sopenharmony_ci vid = 0; 43962306a36Sopenharmony_ci memset(mac, 0, sizeof(mac)); 44062306a36Sopenharmony_ci do { 44162306a36Sopenharmony_ci mutex_lock(&sparx5->lock); 44262306a36Sopenharmony_ci sparx5_mact_select(sparx5, mac, vid); 44362306a36Sopenharmony_ci spx5_wr(LRN_SCAN_NEXT_CFG_SCAN_NEXT_UNTIL_FOUND_ENA_SET(1), 44462306a36Sopenharmony_ci sparx5, LRN_SCAN_NEXT_CFG); 44562306a36Sopenharmony_ci spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET 44662306a36Sopenharmony_ci (MAC_CMD_FIND_SMALLEST) | 44762306a36Sopenharmony_ci LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1), 44862306a36Sopenharmony_ci sparx5, LRN_COMMON_ACCESS_CTRL); 44962306a36Sopenharmony_ci ret = sparx5_mact_wait_for_completion(sparx5); 45062306a36Sopenharmony_ci if (ret == 0) 45162306a36Sopenharmony_ci ret = sparx5_mact_get(sparx5, mac, &vid, &cfg2); 45262306a36Sopenharmony_ci mutex_unlock(&sparx5->lock); 45362306a36Sopenharmony_ci if (ret == 0) 45462306a36Sopenharmony_ci sparx5_mact_handle_entry(sparx5, mac, vid, cfg2); 45562306a36Sopenharmony_ci } while (ret == 0); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci mutex_lock(&sparx5->mact_lock); 45862306a36Sopenharmony_ci list_for_each_entry_safe(mact_entry, tmp, &sparx5->mact_entries, 45962306a36Sopenharmony_ci list) { 46062306a36Sopenharmony_ci /* If the entry is in HW or permanent, then skip */ 46162306a36Sopenharmony_ci if (mact_entry->flags & (MAC_ENT_ALIVE | MAC_ENT_LOCK)) 46262306a36Sopenharmony_ci continue; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci sparx5_fdb_call_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE, 46562306a36Sopenharmony_ci mact_entry->mac, mact_entry->vid, 46662306a36Sopenharmony_ci sparx5->ports[mact_entry->port]->ndev, 46762306a36Sopenharmony_ci true); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci list_del(&mact_entry->list); 47062306a36Sopenharmony_ci devm_kfree(sparx5->dev, mact_entry); 47162306a36Sopenharmony_ci } 47262306a36Sopenharmony_ci mutex_unlock(&sparx5->mact_lock); 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci queue_delayed_work(sparx5->mact_queue, &sparx5->mact_work, 47562306a36Sopenharmony_ci SPX5_MACT_PULL_DELAY); 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_civoid sparx5_set_ageing(struct sparx5 *sparx5, int msecs) 47962306a36Sopenharmony_ci{ 48062306a36Sopenharmony_ci int value = max(1, msecs / 10); /* unit 10 ms */ 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci spx5_rmw(LRN_AUTOAGE_CFG_UNIT_SIZE_SET(2) | /* 10 ms */ 48362306a36Sopenharmony_ci LRN_AUTOAGE_CFG_PERIOD_VAL_SET(value / 2), /* one bit ageing */ 48462306a36Sopenharmony_ci LRN_AUTOAGE_CFG_UNIT_SIZE | 48562306a36Sopenharmony_ci LRN_AUTOAGE_CFG_PERIOD_VAL, 48662306a36Sopenharmony_ci sparx5, 48762306a36Sopenharmony_ci LRN_AUTOAGE_CFG(0)); 48862306a36Sopenharmony_ci} 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_civoid sparx5_mact_init(struct sparx5 *sparx5) 49162306a36Sopenharmony_ci{ 49262306a36Sopenharmony_ci mutex_init(&sparx5->lock); 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci /* Flush MAC table */ 49562306a36Sopenharmony_ci spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_CLEAR_ALL) | 49662306a36Sopenharmony_ci LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1), 49762306a36Sopenharmony_ci sparx5, LRN_COMMON_ACCESS_CTRL); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci if (sparx5_mact_wait_for_completion(sparx5) != 0) 50062306a36Sopenharmony_ci dev_warn(sparx5->dev, "MAC flush error\n"); 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci sparx5_set_ageing(sparx5, BR_DEFAULT_AGEING_TIME / HZ * 1000); 50362306a36Sopenharmony_ci} 504