Commit 25ee9561 authored by Horatiu Vultur's avatar Horatiu Vultur Committed by David S. Miller
Browse files

net: lan966x: More MAC table functionality



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: default avatarHoratiu Vultur <horatiu.vultur@microchip.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 5ccd66e0
Loading
Loading
Loading
Loading
+105 −0
Original line number Diff line number Diff line
@@ -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;
+9 −0
Original line number Diff line number Diff line
@@ -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);