brintos

brintos / linux-shallow public Read only

0
0
Text · 13.0 KiB · 75868b3 Raw
504 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/* Microchip Sparx5 Switch driver3 *4 * Copyright (c) 2021 Microchip Technology Inc. and its subsidiaries.5 */6 7#include <net/switchdev.h>8#include <linux/if_bridge.h>9#include <linux/iopoll.h>10 11#include "sparx5_main_regs.h"12#include "sparx5_main.h"13 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 */23 24/* Commands for MAC_ENTRY_ADDR_TYPE */25#define  MAC_ENTRY_ADDR_TYPE_UPSID_PN         026#define  MAC_ENTRY_ADDR_TYPE_UPSID_CPU_OR_INT 127#define  MAC_ENTRY_ADDR_TYPE_GLAG             228#define  MAC_ENTRY_ADDR_TYPE_MC_IDX           329 30#define TABLE_UPDATE_SLEEP_US 1031#define TABLE_UPDATE_TIMEOUT_US 10000032 33struct 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;42};43 44static int sparx5_mact_get_status(struct sparx5 *sparx5)45{46	return spx5_rd(sparx5, LRN_COMMON_ACCESS_CTRL);47}48 49static int sparx5_mact_wait_for_completion(struct sparx5 *sparx5)50{51	u32 val;52 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);57}58 59static void sparx5_mact_select(struct sparx5 *sparx5,60			       const unsigned char mac[ETH_ALEN],61			       u16 vid)62{63	u32 macl = 0, mach = 0;64 65	/* Set the MAC address to handle and the vlan associated in a format66	 * understood by the hardware.67	 */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;75 76	spx5_wr(mach, sparx5, LRN_MAC_ACCESS_CFG_0);77	spx5_wr(macl, sparx5, LRN_MAC_ACCESS_CFG_1);78}79 80int sparx5_mact_learn(struct sparx5 *sparx5, int pgid,81		      const unsigned char mac[ETH_ALEN], u16 vid)82{83	int addr, type, ret;84 85	if (pgid < SPX5_PORTS) {86		type = MAC_ENTRY_ADDR_TYPE_UPSID_PN;87		addr = pgid % 32;88		addr += (pgid / 32) << 5; /* Add upsid */89	} else {90		type = MAC_ENTRY_ADDR_TYPE_MC_IDX;91		addr = pgid - SPX5_PORTS;92	}93 94	mutex_lock(&sparx5->lock);95 96	sparx5_mact_select(sparx5, mac, vid);97 98	/* MAC entry properties */99	spx5_wr(LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_SET(addr) |100		LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_TYPE_SET(type) |101		LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_SET(1) |102		LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_LOCKED_SET(1),103		sparx5, LRN_MAC_ACCESS_CFG_2);104	spx5_wr(0, sparx5, LRN_MAC_ACCESS_CFG_3);105 106	/*  Insert/learn new entry */107	spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_LEARN) |108		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),109		sparx5, LRN_COMMON_ACCESS_CTRL);110 111	ret = sparx5_mact_wait_for_completion(sparx5);112 113	mutex_unlock(&sparx5->lock);114 115	return ret;116}117 118int sparx5_mc_unsync(struct net_device *dev, const unsigned char *addr)119{120	struct sparx5_port *port = netdev_priv(dev);121	struct sparx5 *sparx5 = port->sparx5;122 123	return sparx5_mact_forget(sparx5, addr, port->pvid);124}125 126int sparx5_mc_sync(struct net_device *dev, const unsigned char *addr)127{128	struct sparx5_port *port = netdev_priv(dev);129	struct sparx5 *sparx5 = port->sparx5;130 131	return sparx5_mact_learn(sparx5, PGID_CPU, addr, port->pvid);132}133 134static int sparx5_mact_get(struct sparx5 *sparx5,135			   unsigned char mac[ETH_ALEN],136			   u16 *vid, u32 *pcfg2)137{138	u32 mach, macl, cfg2;139	int ret = -ENOENT;140 141	cfg2 = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_2);142	if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_GET(cfg2)) {143		mach = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_0);144		macl = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_1);145		mac[0] = ((mach >> 8)  & 0xff);146		mac[1] = ((mach >> 0)  & 0xff);147		mac[2] = ((macl >> 24) & 0xff);148		mac[3] = ((macl >> 16) & 0xff);149		mac[4] = ((macl >> 8)  & 0xff);150		mac[5] = ((macl >> 0)  & 0xff);151		*vid = mach >> 16;152		*pcfg2 = cfg2;153		ret = 0;154	}155 156	return ret;157}158 159bool sparx5_mact_getnext(struct sparx5 *sparx5,160			 unsigned char mac[ETH_ALEN], u16 *vid, u32 *pcfg2)161{162	u32 cfg2;163	int ret;164 165	mutex_lock(&sparx5->lock);166 167	sparx5_mact_select(sparx5, mac, *vid);168 169	spx5_wr(LRN_SCAN_NEXT_CFG_SCAN_NEXT_IGNORE_LOCKED_ENA_SET(1) |170		LRN_SCAN_NEXT_CFG_SCAN_NEXT_UNTIL_FOUND_ENA_SET(1),171		sparx5, LRN_SCAN_NEXT_CFG);172	spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET173		(MAC_CMD_FIND_SMALLEST) |174		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),175		sparx5, LRN_COMMON_ACCESS_CTRL);176 177	ret = sparx5_mact_wait_for_completion(sparx5);178	if (ret == 0) {179		ret = sparx5_mact_get(sparx5, mac, vid, &cfg2);180		if (ret == 0)181			*pcfg2 = cfg2;182	}183 184	mutex_unlock(&sparx5->lock);185 186	return ret == 0;187}188 189int sparx5_mact_find(struct sparx5 *sparx5,190		     const unsigned char mac[ETH_ALEN], u16 vid, u32 *pcfg2)191{192	int ret;193	u32 cfg2;194 195	mutex_lock(&sparx5->lock);196 197	sparx5_mact_select(sparx5, mac, vid);198 199	/* Issue a lookup command */200	spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_LOOKUP) |201		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),202		sparx5, LRN_COMMON_ACCESS_CTRL);203 204	ret = sparx5_mact_wait_for_completion(sparx5);205	if (ret == 0) {206		cfg2 = spx5_rd(sparx5, LRN_MAC_ACCESS_CFG_2);207		if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_VLD_GET(cfg2))208			*pcfg2 = cfg2;209		else210			ret = -ENOENT;211	}212 213	mutex_unlock(&sparx5->lock);214 215	return ret;216}217 218int sparx5_mact_forget(struct sparx5 *sparx5,219		       const unsigned char mac[ETH_ALEN], u16 vid)220{221	int ret;222 223	mutex_lock(&sparx5->lock);224 225	sparx5_mact_select(sparx5, mac, vid);226 227	/* Issue an unlearn command */228	spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_UNLEARN) |229		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),230		sparx5, LRN_COMMON_ACCESS_CTRL);231 232	ret = sparx5_mact_wait_for_completion(sparx5);233 234	mutex_unlock(&sparx5->lock);235 236	return ret;237}238 239static struct sparx5_mact_entry *alloc_mact_entry(struct sparx5 *sparx5,240						  const unsigned char *mac,241						  u16 vid, u16 port_index)242{243	struct sparx5_mact_entry *mact_entry;244 245	mact_entry = devm_kzalloc(sparx5->dev,246				  sizeof(*mact_entry), GFP_ATOMIC);247	if (!mact_entry)248		return NULL;249 250	memcpy(mact_entry->mac, mac, ETH_ALEN);251	mact_entry->vid = vid;252	mact_entry->port = port_index;253	return mact_entry;254}255 256static struct sparx5_mact_entry *find_mact_entry(struct sparx5 *sparx5,257						 const unsigned char *mac,258						 u16 vid, u16 port_index)259{260	struct sparx5_mact_entry *mact_entry;261	struct sparx5_mact_entry *res = NULL;262 263	mutex_lock(&sparx5->mact_lock);264	list_for_each_entry(mact_entry, &sparx5->mact_entries, list) {265		if (mact_entry->vid == vid &&266		    ether_addr_equal(mac, mact_entry->mac) &&267		    mact_entry->port == port_index) {268			res = mact_entry;269			break;270		}271	}272	mutex_unlock(&sparx5->mact_lock);273 274	return res;275}276 277static void sparx5_fdb_call_notifiers(enum switchdev_notifier_type type,278				      const char *mac, u16 vid,279				      struct net_device *dev, bool offloaded)280{281	struct switchdev_notifier_fdb_info info = {};282 283	info.addr = mac;284	info.vid = vid;285	info.offloaded = offloaded;286	call_switchdev_notifiers(type, dev, &info.info, NULL);287}288 289int sparx5_add_mact_entry(struct sparx5 *sparx5,290			  struct net_device *dev,291			  u16 portno,292			  const unsigned char *addr, u16 vid)293{294	struct sparx5_mact_entry *mact_entry;295	int ret;296	u32 cfg2;297 298	ret = sparx5_mact_find(sparx5, addr, vid, &cfg2);299	if (!ret)300		return 0;301 302	/* In case the entry already exists, don't add it again to SW,303	 * just update HW, but we need to look in the actual HW because304	 * it is possible for an entry to be learn by HW and before the305	 * mact thread to start the frame will reach CPU and the CPU will306	 * add the entry but without the extern_learn flag.307	 */308	mact_entry = find_mact_entry(sparx5, addr, vid, portno);309	if (mact_entry)310		goto update_hw;311 312	/* Add the entry in SW MAC table not to get the notification when313	 * SW is pulling again314	 */315	mact_entry = alloc_mact_entry(sparx5, addr, vid, portno);316	if (!mact_entry)317		return -ENOMEM;318 319	mutex_lock(&sparx5->mact_lock);320	list_add_tail(&mact_entry->list, &sparx5->mact_entries);321	mutex_unlock(&sparx5->mact_lock);322 323update_hw:324	ret = sparx5_mact_learn(sparx5, portno, addr, vid);325 326	/* New entry? */327	if (mact_entry->flags == 0) {328		mact_entry->flags |= MAC_ENT_LOCK; /* Don't age this */329		sparx5_fdb_call_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE, addr, vid,330					  dev, true);331	}332 333	return ret;334}335 336int sparx5_del_mact_entry(struct sparx5 *sparx5,337			  const unsigned char *addr,338			  u16 vid)339{340	struct sparx5_mact_entry *mact_entry, *tmp;341 342	/* Delete the entry in SW MAC table not to get the notification when343	 * SW is pulling again344	 */345	mutex_lock(&sparx5->mact_lock);346	list_for_each_entry_safe(mact_entry, tmp, &sparx5->mact_entries,347				 list) {348		if ((vid == 0 || mact_entry->vid == vid) &&349		    ether_addr_equal(addr, mact_entry->mac)) {350			sparx5_mact_forget(sparx5, addr, mact_entry->vid);351 352			list_del(&mact_entry->list);353			devm_kfree(sparx5->dev, mact_entry);354		}355	}356	mutex_unlock(&sparx5->mact_lock);357 358	return 0;359}360 361static void sparx5_mact_handle_entry(struct sparx5 *sparx5,362				     unsigned char mac[ETH_ALEN],363				     u16 vid, u32 cfg2)364{365	struct sparx5_mact_entry *mact_entry;366	bool found = false;367	u16 port;368 369	if (LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_TYPE_GET(cfg2) !=370	    MAC_ENTRY_ADDR_TYPE_UPSID_PN)371		return;372 373	port = LRN_MAC_ACCESS_CFG_2_MAC_ENTRY_ADDR_GET(cfg2);374	if (port >= SPX5_PORTS)375		return;376 377	if (!test_bit(port, sparx5->bridge_mask))378		return;379 380	mutex_lock(&sparx5->mact_lock);381	list_for_each_entry(mact_entry, &sparx5->mact_entries, list) {382		if (mact_entry->vid == vid &&383		    ether_addr_equal(mac, mact_entry->mac)) {384			found = true;385			mact_entry->flags |= MAC_ENT_ALIVE;386			if (mact_entry->port != port) {387				dev_warn(sparx5->dev, "Entry move: %d -> %d\n",388					 mact_entry->port, port);389				mact_entry->port = port;390				mact_entry->flags |= MAC_ENT_MOVED;391			}392			/* Entry handled */393			break;394		}395	}396	mutex_unlock(&sparx5->mact_lock);397 398	if (found && !(mact_entry->flags & MAC_ENT_MOVED))399		/* Present, not moved */400		return;401 402	if (!found) {403		/* Entry not found - now add */404		mact_entry = alloc_mact_entry(sparx5, mac, vid, port);405		if (!mact_entry)406			return;407 408		mact_entry->flags |= MAC_ENT_ALIVE;409		mutex_lock(&sparx5->mact_lock);410		list_add_tail(&mact_entry->list, &sparx5->mact_entries);411		mutex_unlock(&sparx5->mact_lock);412	}413 414	/* New or moved entry - notify bridge */415	sparx5_fdb_call_notifiers(SWITCHDEV_FDB_ADD_TO_BRIDGE,416				  mac, vid, sparx5->ports[port]->ndev,417				  true);418}419 420void sparx5_mact_pull_work(struct work_struct *work)421{422	struct delayed_work *del_work = to_delayed_work(work);423	struct sparx5 *sparx5 = container_of(del_work, struct sparx5,424					     mact_work);425	struct sparx5_mact_entry *mact_entry, *tmp;426	unsigned char mac[ETH_ALEN];427	u32 cfg2;428	u16 vid;429	int ret;430 431	/* Reset MAC entry flags */432	mutex_lock(&sparx5->mact_lock);433	list_for_each_entry(mact_entry, &sparx5->mact_entries, list)434		mact_entry->flags &= MAC_ENT_LOCK;435	mutex_unlock(&sparx5->mact_lock);436 437	/* MAIN mac address processing loop */438	vid = 0;439	memset(mac, 0, sizeof(mac));440	do {441		mutex_lock(&sparx5->lock);442		sparx5_mact_select(sparx5, mac, vid);443		spx5_wr(LRN_SCAN_NEXT_CFG_SCAN_NEXT_UNTIL_FOUND_ENA_SET(1),444			sparx5, LRN_SCAN_NEXT_CFG);445		spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET446			(MAC_CMD_FIND_SMALLEST) |447			LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),448			sparx5, LRN_COMMON_ACCESS_CTRL);449		ret = sparx5_mact_wait_for_completion(sparx5);450		if (ret == 0)451			ret = sparx5_mact_get(sparx5, mac, &vid, &cfg2);452		mutex_unlock(&sparx5->lock);453		if (ret == 0)454			sparx5_mact_handle_entry(sparx5, mac, vid, cfg2);455	} while (ret == 0);456 457	mutex_lock(&sparx5->mact_lock);458	list_for_each_entry_safe(mact_entry, tmp, &sparx5->mact_entries,459				 list) {460		/* If the entry is in HW or permanent, then skip */461		if (mact_entry->flags & (MAC_ENT_ALIVE | MAC_ENT_LOCK))462			continue;463 464		sparx5_fdb_call_notifiers(SWITCHDEV_FDB_DEL_TO_BRIDGE,465					  mact_entry->mac, mact_entry->vid,466					  sparx5->ports[mact_entry->port]->ndev,467					  true);468 469		list_del(&mact_entry->list);470		devm_kfree(sparx5->dev, mact_entry);471	}472	mutex_unlock(&sparx5->mact_lock);473 474	queue_delayed_work(sparx5->mact_queue, &sparx5->mact_work,475			   SPX5_MACT_PULL_DELAY);476}477 478void sparx5_set_ageing(struct sparx5 *sparx5, int msecs)479{480	int value = max(1, msecs / 10); /* unit 10 ms */481 482	spx5_rmw(LRN_AUTOAGE_CFG_UNIT_SIZE_SET(2) | /* 10 ms */483		 LRN_AUTOAGE_CFG_PERIOD_VAL_SET(value / 2), /* one bit ageing */484		 LRN_AUTOAGE_CFG_UNIT_SIZE |485		 LRN_AUTOAGE_CFG_PERIOD_VAL,486		 sparx5,487		 LRN_AUTOAGE_CFG(0));488}489 490void sparx5_mact_init(struct sparx5 *sparx5)491{492	mutex_init(&sparx5->lock);493 494	/*  Flush MAC table */495	spx5_wr(LRN_COMMON_ACCESS_CTRL_CPU_ACCESS_CMD_SET(MAC_CMD_CLEAR_ALL) |496		LRN_COMMON_ACCESS_CTRL_MAC_TABLE_ACCESS_SHOT_SET(1),497		sparx5, LRN_COMMON_ACCESS_CTRL);498 499	if (sparx5_mact_wait_for_completion(sparx5) != 0)500		dev_warn(sparx5->dev, "MAC flush error\n");501 502	sparx5_set_ageing(sparx5, BR_DEFAULT_AGEING_TIME / HZ * 1000);503}504