From b22da3d8526a935aa31e086e63f60ff3246cb61c Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Sat, 09 Dec 2023 07:24:11 +0000
Subject: [PATCH] add stmac read mac form eeprom

---
 kernel/drivers/staging/fsl-dpaa2/ethsw/ethsw.c |  949 ++++++++++++++++++++++++++++++++++++++++------------------
 1 files changed, 646 insertions(+), 303 deletions(-)

diff --git a/kernel/drivers/staging/fsl-dpaa2/ethsw/ethsw.c b/kernel/drivers/staging/fsl-dpaa2/ethsw/ethsw.c
index 8549e80..20c6326 100644
--- a/kernel/drivers/staging/fsl-dpaa2/ethsw/ethsw.c
+++ b/kernel/drivers/staging/fsl-dpaa2/ethsw/ethsw.c
@@ -18,26 +18,19 @@
 
 #include "ethsw.h"
 
-static struct workqueue_struct *ethsw_owq;
-
 /* Minimal supported DPSW version */
 #define DPSW_MIN_VER_MAJOR		8
-#define DPSW_MIN_VER_MINOR		0
+#define DPSW_MIN_VER_MINOR		1
 
 #define DEFAULT_VLAN_ID			1
 
-static int ethsw_add_vlan(struct ethsw_core *ethsw, u16 vid)
+static int dpaa2_switch_add_vlan(struct ethsw_core *ethsw, u16 vid)
 {
 	int err;
 
 	struct dpsw_vlan_cfg	vcfg = {
 		.fdb_id = 0,
 	};
-
-	if (ethsw->vlans[vid]) {
-		dev_err(ethsw->dev, "VLAN already configured\n");
-		return -EEXIST;
-	}
 
 	err = dpsw_vlan_add(ethsw->mc_io, 0,
 			    ethsw->dpsw_handle, vid, &vcfg);
@@ -50,12 +43,31 @@
 	return 0;
 }
 
-static int ethsw_port_set_pvid(struct ethsw_port_priv *port_priv, u16 pvid)
+static bool dpaa2_switch_port_is_up(struct ethsw_port_priv *port_priv)
+{
+	struct net_device *netdev = port_priv->netdev;
+	struct dpsw_link_state state;
+	int err;
+
+	err = dpsw_if_get_link_state(port_priv->ethsw_data->mc_io, 0,
+				     port_priv->ethsw_data->dpsw_handle,
+				     port_priv->idx, &state);
+	if (err) {
+		netdev_err(netdev, "dpsw_if_get_link_state() err %d\n", err);
+		return true;
+	}
+
+	WARN_ONCE(state.up > 1, "Garbage read into link_state");
+
+	return state.up ? true : false;
+}
+
+static int dpaa2_switch_port_set_pvid(struct ethsw_port_priv *port_priv, u16 pvid)
 {
 	struct ethsw_core *ethsw = port_priv->ethsw_data;
 	struct net_device *netdev = port_priv->netdev;
 	struct dpsw_tci_cfg tci_cfg = { 0 };
-	bool is_oper;
+	bool up;
 	int err, ret;
 
 	err = dpsw_if_get_tci(ethsw->mc_io, 0, ethsw->dpsw_handle,
@@ -68,8 +80,8 @@
 	tci_cfg.vlan_id = pvid;
 
 	/* Interface needs to be down to change PVID */
-	is_oper = netif_oper_up(netdev);
-	if (is_oper) {
+	up = dpaa2_switch_port_is_up(port_priv);
+	if (up) {
 		err = dpsw_if_disable(ethsw->mc_io, 0,
 				      ethsw->dpsw_handle,
 				      port_priv->idx);
@@ -92,7 +104,7 @@
 	port_priv->pvid = pvid;
 
 set_tci_error:
-	if (is_oper) {
+	if (up) {
 		ret = dpsw_if_enable(ethsw->mc_io, 0,
 				     ethsw->dpsw_handle,
 				     port_priv->idx);
@@ -105,8 +117,8 @@
 	return err;
 }
 
-static int ethsw_port_add_vlan(struct ethsw_port_priv *port_priv,
-			       u16 vid, u16 flags)
+static int dpaa2_switch_port_add_vlan(struct ethsw_port_priv *port_priv,
+				      u16 vid, u16 flags)
 {
 	struct ethsw_core *ethsw = port_priv->ethsw_data;
 	struct net_device *netdev = port_priv->netdev;
@@ -141,7 +153,7 @@
 	}
 
 	if (flags & BRIDGE_VLAN_INFO_PVID) {
-		err = ethsw_port_set_pvid(port_priv, vid);
+		err = dpaa2_switch_port_set_pvid(port_priv, vid);
 		if (err)
 			return err;
 	}
@@ -149,12 +161,12 @@
 	return 0;
 }
 
-static int ethsw_set_learning(struct ethsw_core *ethsw, u8 flag)
+static int dpaa2_switch_set_learning(struct ethsw_core *ethsw, bool enable)
 {
 	enum dpsw_fdb_learning_mode learn_mode;
 	int err;
 
-	if (flag)
+	if (enable)
 		learn_mode = DPSW_FDB_LEARNING_MODE_HW;
 	else
 		learn_mode = DPSW_FDB_LEARNING_MODE_DIS;
@@ -165,46 +177,51 @@
 		dev_err(ethsw->dev, "dpsw_fdb_set_learning_mode err %d\n", err);
 		return err;
 	}
-	ethsw->learning = !!flag;
+	ethsw->learning = enable;
 
 	return 0;
 }
 
-static int ethsw_port_set_flood(struct ethsw_port_priv *port_priv, u8 flag)
+static int dpaa2_switch_port_set_flood(struct ethsw_port_priv *port_priv, bool enable)
 {
 	int err;
 
 	err = dpsw_if_set_flooding(port_priv->ethsw_data->mc_io, 0,
 				   port_priv->ethsw_data->dpsw_handle,
-				   port_priv->idx, flag);
+				   port_priv->idx, enable);
 	if (err) {
 		netdev_err(port_priv->netdev,
 			   "dpsw_if_set_flooding err %d\n", err);
 		return err;
 	}
-	port_priv->flood = !!flag;
+	port_priv->flood = enable;
 
 	return 0;
 }
 
-static int ethsw_port_set_stp_state(struct ethsw_port_priv *port_priv, u8 state)
+static int dpaa2_switch_port_set_stp_state(struct ethsw_port_priv *port_priv, u8 state)
 {
 	struct dpsw_stp_cfg stp_cfg = {
-		.vlan_id = DEFAULT_VLAN_ID,
 		.state = state,
 	};
 	int err;
+	u16 vid;
 
-	if (!netif_oper_up(port_priv->netdev) || state == port_priv->stp_state)
+	if (!netif_running(port_priv->netdev) || state == port_priv->stp_state)
 		return 0;	/* Nothing to do */
 
-	err = dpsw_if_set_stp(port_priv->ethsw_data->mc_io, 0,
-			      port_priv->ethsw_data->dpsw_handle,
-			      port_priv->idx, &stp_cfg);
-	if (err) {
-		netdev_err(port_priv->netdev,
-			   "dpsw_if_set_stp err %d\n", err);
-		return err;
+	for (vid = 0; vid <= VLAN_VID_MASK; vid++) {
+		if (port_priv->vlans[vid] & ETHSW_VLAN_MEMBER) {
+			stp_cfg.vlan_id = vid;
+			err = dpsw_if_set_stp(port_priv->ethsw_data->mc_io, 0,
+					      port_priv->ethsw_data->dpsw_handle,
+					      port_priv->idx, &stp_cfg);
+			if (err) {
+				netdev_err(port_priv->netdev,
+					   "dpsw_if_set_stp err %d\n", err);
+				return err;
+			}
+		}
 	}
 
 	port_priv->stp_state = state;
@@ -212,7 +229,7 @@
 	return 0;
 }
 
-static int ethsw_dellink_switch(struct ethsw_core *ethsw, u16 vid)
+static int dpaa2_switch_dellink(struct ethsw_core *ethsw, u16 vid)
 {
 	struct ethsw_port_priv *ppriv_local = NULL;
 	int i, err;
@@ -235,8 +252,8 @@
 	return 0;
 }
 
-static int ethsw_port_fdb_add_uc(struct ethsw_port_priv *port_priv,
-				 const unsigned char *addr)
+static int dpaa2_switch_port_fdb_add_uc(struct ethsw_port_priv *port_priv,
+					const unsigned char *addr)
 {
 	struct dpsw_fdb_unicast_cfg entry = {0};
 	int err;
@@ -254,8 +271,8 @@
 	return err;
 }
 
-static int ethsw_port_fdb_del_uc(struct ethsw_port_priv *port_priv,
-				 const unsigned char *addr)
+static int dpaa2_switch_port_fdb_del_uc(struct ethsw_port_priv *port_priv,
+					const unsigned char *addr)
 {
 	struct dpsw_fdb_unicast_cfg entry = {0};
 	int err;
@@ -274,8 +291,8 @@
 	return err;
 }
 
-static int ethsw_port_fdb_add_mc(struct ethsw_port_priv *port_priv,
-				 const unsigned char *addr)
+static int dpaa2_switch_port_fdb_add_mc(struct ethsw_port_priv *port_priv,
+					const unsigned char *addr)
 {
 	struct dpsw_fdb_multicast_cfg entry = {0};
 	int err;
@@ -295,8 +312,8 @@
 	return err;
 }
 
-static int ethsw_port_fdb_del_mc(struct ethsw_port_priv *port_priv,
-				 const unsigned char *addr)
+static int dpaa2_switch_port_fdb_del_mc(struct ethsw_port_priv *port_priv,
+					const unsigned char *addr)
 {
 	struct dpsw_fdb_multicast_cfg entry = {0};
 	int err;
@@ -316,8 +333,33 @@
 	return err;
 }
 
-static void port_get_stats(struct net_device *netdev,
-			   struct rtnl_link_stats64 *stats)
+static int dpaa2_switch_port_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
+				     struct net_device *dev, const unsigned char *addr,
+				     u16 vid, u16 flags,
+				     struct netlink_ext_ack *extack)
+{
+	if (is_unicast_ether_addr(addr))
+		return dpaa2_switch_port_fdb_add_uc(netdev_priv(dev),
+						    addr);
+	else
+		return dpaa2_switch_port_fdb_add_mc(netdev_priv(dev),
+						    addr);
+}
+
+static int dpaa2_switch_port_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],
+				     struct net_device *dev,
+				     const unsigned char *addr, u16 vid)
+{
+	if (is_unicast_ether_addr(addr))
+		return dpaa2_switch_port_fdb_del_uc(netdev_priv(dev),
+						    addr);
+	else
+		return dpaa2_switch_port_fdb_del_mc(netdev_priv(dev),
+						    addr);
+}
+
+static void dpaa2_switch_port_get_stats(struct net_device *netdev,
+					struct rtnl_link_stats64 *stats)
 {
 	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
 	u64 tmp;
@@ -382,26 +424,26 @@
 	netdev_err(netdev, "dpsw_if_get_counter err %d\n", err);
 }
 
-static bool port_has_offload_stats(const struct net_device *netdev,
-				   int attr_id)
+static bool dpaa2_switch_port_has_offload_stats(const struct net_device *netdev,
+						int attr_id)
 {
 	return (attr_id == IFLA_OFFLOAD_XSTATS_CPU_HIT);
 }
 
-static int port_get_offload_stats(int attr_id,
-				  const struct net_device *netdev,
-				  void *sp)
+static int dpaa2_switch_port_get_offload_stats(int attr_id,
+					       const struct net_device *netdev,
+					       void *sp)
 {
 	switch (attr_id) {
 	case IFLA_OFFLOAD_XSTATS_CPU_HIT:
-		port_get_stats((struct net_device *)netdev, sp);
+		dpaa2_switch_port_get_stats((struct net_device *)netdev, sp);
 		return 0;
 	}
 
 	return -EINVAL;
 }
 
-static int port_change_mtu(struct net_device *netdev, int mtu)
+static int dpaa2_switch_port_change_mtu(struct net_device *netdev, int mtu)
 {
 	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
 	int err;
@@ -421,11 +463,17 @@
 	return 0;
 }
 
-static int port_carrier_state_sync(struct net_device *netdev)
+static int dpaa2_switch_port_carrier_state_sync(struct net_device *netdev)
 {
 	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
 	struct dpsw_link_state state;
 	int err;
+
+	/* Interrupts are received even though no one issued an 'ifconfig up'
+	 * on the switch interface. Ignore these link state update interrupts
+	 */
+	if (!netif_running(netdev))
+		return 0;
 
 	err = dpsw_if_get_link_state(port_priv->ethsw_data->mc_io, 0,
 				     port_priv->ethsw_data->dpsw_handle,
@@ -444,16 +492,24 @@
 			netif_carrier_off(netdev);
 		port_priv->link_state = state.up;
 	}
+
 	return 0;
 }
 
-static int port_open(struct net_device *netdev)
+static int dpaa2_switch_port_open(struct net_device *netdev)
 {
 	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
 	int err;
 
 	/* No need to allow Tx as control interface is disabled */
 	netif_tx_stop_all_queues(netdev);
+
+	/* Explicitly set carrier off, otherwise
+	 * netif_carrier_ok() will return true and cause 'ip link show'
+	 * to report the LOWER_UP flag, even though the link
+	 * notification wasn't even received.
+	 */
+	netif_carrier_off(netdev);
 
 	err = dpsw_if_enable(port_priv->ethsw_data->mc_io, 0,
 			     port_priv->ethsw_data->dpsw_handle,
@@ -464,10 +520,10 @@
 	}
 
 	/* sync carrier state */
-	err = port_carrier_state_sync(netdev);
+	err = dpaa2_switch_port_carrier_state_sync(netdev);
 	if (err) {
 		netdev_err(netdev,
-			   "port_carrier_state_sync err %d\n", err);
+			   "dpaa2_switch_port_carrier_state_sync err %d\n", err);
 		goto err_carrier_sync;
 	}
 
@@ -480,7 +536,7 @@
 	return err;
 }
 
-static int port_stop(struct net_device *netdev)
+static int dpaa2_switch_port_stop(struct net_device *netdev)
 {
 	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
 	int err;
@@ -496,8 +552,8 @@
 	return 0;
 }
 
-static netdev_tx_t port_dropframe(struct sk_buff *skb,
-				  struct net_device *netdev)
+static netdev_tx_t dpaa2_switch_port_dropframe(struct sk_buff *skb,
+					       struct net_device *netdev)
 {
 	/* we don't support I/O for now, drop the frame */
 	dev_kfree_skb_any(skb);
@@ -505,27 +561,243 @@
 	return NETDEV_TX_OK;
 }
 
-static const struct net_device_ops ethsw_port_ops = {
-	.ndo_open		= port_open,
-	.ndo_stop		= port_stop,
+static int dpaa2_switch_port_parent_id(struct net_device *dev,
+				       struct netdev_phys_item_id *ppid)
+{
+	struct ethsw_port_priv *port_priv = netdev_priv(dev);
 
-	.ndo_set_mac_address	= eth_mac_addr,
-	.ndo_change_mtu		= port_change_mtu,
-	.ndo_has_offload_stats	= port_has_offload_stats,
-	.ndo_get_offload_stats	= port_get_offload_stats,
+	ppid->id_len = 1;
+	ppid->id[0] = port_priv->ethsw_data->dev_id;
 
-	.ndo_start_xmit		= port_dropframe,
+	return 0;
+}
+
+static int dpaa2_switch_port_get_phys_name(struct net_device *netdev, char *name,
+					   size_t len)
+{
+	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+	int err;
+
+	err = snprintf(name, len, "p%d", port_priv->idx);
+	if (err >= len)
+		return -EINVAL;
+
+	return 0;
+}
+
+struct ethsw_dump_ctx {
+	struct net_device *dev;
+	struct sk_buff *skb;
+	struct netlink_callback *cb;
+	int idx;
 };
 
-static void ethsw_links_state_update(struct ethsw_core *ethsw)
+static int dpaa2_switch_fdb_dump_nl(struct fdb_dump_entry *entry,
+				    struct ethsw_dump_ctx *dump)
+{
+	int is_dynamic = entry->type & DPSW_FDB_ENTRY_DINAMIC;
+	u32 portid = NETLINK_CB(dump->cb->skb).portid;
+	u32 seq = dump->cb->nlh->nlmsg_seq;
+	struct nlmsghdr *nlh;
+	struct ndmsg *ndm;
+
+	if (dump->idx < dump->cb->args[2])
+		goto skip;
+
+	nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH,
+			sizeof(*ndm), NLM_F_MULTI);
+	if (!nlh)
+		return -EMSGSIZE;
+
+	ndm = nlmsg_data(nlh);
+	ndm->ndm_family  = AF_BRIDGE;
+	ndm->ndm_pad1    = 0;
+	ndm->ndm_pad2    = 0;
+	ndm->ndm_flags   = NTF_SELF;
+	ndm->ndm_type    = 0;
+	ndm->ndm_ifindex = dump->dev->ifindex;
+	ndm->ndm_state   = is_dynamic ? NUD_REACHABLE : NUD_NOARP;
+
+	if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, entry->mac_addr))
+		goto nla_put_failure;
+
+	nlmsg_end(dump->skb, nlh);
+
+skip:
+	dump->idx++;
+	return 0;
+
+nla_put_failure:
+	nlmsg_cancel(dump->skb, nlh);
+	return -EMSGSIZE;
+}
+
+static int dpaa2_switch_port_fdb_valid_entry(struct fdb_dump_entry *entry,
+					     struct ethsw_port_priv *port_priv)
+{
+	int idx = port_priv->idx;
+	int valid;
+
+	if (entry->type & DPSW_FDB_ENTRY_TYPE_UNICAST)
+		valid = entry->if_info == port_priv->idx;
+	else
+		valid = entry->if_mask[idx / 8] & BIT(idx % 8);
+
+	return valid;
+}
+
+static int dpaa2_switch_port_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
+				      struct net_device *net_dev,
+				      struct net_device *filter_dev, int *idx)
+{
+	struct ethsw_port_priv *port_priv = netdev_priv(net_dev);
+	struct ethsw_core *ethsw = port_priv->ethsw_data;
+	struct device *dev = net_dev->dev.parent;
+	struct fdb_dump_entry *fdb_entries;
+	struct fdb_dump_entry fdb_entry;
+	struct ethsw_dump_ctx dump = {
+		.dev = net_dev,
+		.skb = skb,
+		.cb = cb,
+		.idx = *idx,
+	};
+	dma_addr_t fdb_dump_iova;
+	u16 num_fdb_entries;
+	u32 fdb_dump_size;
+	int err = 0, i;
+	u8 *dma_mem;
+
+	fdb_dump_size = ethsw->sw_attr.max_fdb_entries * sizeof(fdb_entry);
+	dma_mem = kzalloc(fdb_dump_size, GFP_KERNEL);
+	if (!dma_mem)
+		return -ENOMEM;
+
+	fdb_dump_iova = dma_map_single(dev, dma_mem, fdb_dump_size,
+				       DMA_FROM_DEVICE);
+	if (dma_mapping_error(dev, fdb_dump_iova)) {
+		netdev_err(net_dev, "dma_map_single() failed\n");
+		err = -ENOMEM;
+		goto err_map;
+	}
+
+	err = dpsw_fdb_dump(ethsw->mc_io, 0, ethsw->dpsw_handle, 0,
+			    fdb_dump_iova, fdb_dump_size, &num_fdb_entries);
+	if (err) {
+		netdev_err(net_dev, "dpsw_fdb_dump() = %d\n", err);
+		goto err_dump;
+	}
+
+	dma_unmap_single(dev, fdb_dump_iova, fdb_dump_size, DMA_FROM_DEVICE);
+
+	fdb_entries = (struct fdb_dump_entry *)dma_mem;
+	for (i = 0; i < num_fdb_entries; i++) {
+		fdb_entry = fdb_entries[i];
+
+		if (!dpaa2_switch_port_fdb_valid_entry(&fdb_entry, port_priv))
+			continue;
+
+		err = dpaa2_switch_fdb_dump_nl(&fdb_entry, &dump);
+		if (err)
+			goto end;
+	}
+
+end:
+	*idx = dump.idx;
+
+	kfree(dma_mem);
+
+	return 0;
+
+err_dump:
+	dma_unmap_single(dev, fdb_dump_iova, fdb_dump_size, DMA_TO_DEVICE);
+err_map:
+	kfree(dma_mem);
+	return err;
+}
+
+static int dpaa2_switch_port_set_mac_addr(struct ethsw_port_priv *port_priv)
+{
+	struct ethsw_core *ethsw = port_priv->ethsw_data;
+	struct net_device *net_dev = port_priv->netdev;
+	struct device *dev = net_dev->dev.parent;
+	u8 mac_addr[ETH_ALEN];
+	int err;
+
+	if (!(ethsw->features & ETHSW_FEATURE_MAC_ADDR))
+		return 0;
+
+	/* Get firmware address, if any */
+	err = dpsw_if_get_port_mac_addr(ethsw->mc_io, 0, ethsw->dpsw_handle,
+					port_priv->idx, mac_addr);
+	if (err) {
+		dev_err(dev, "dpsw_if_get_port_mac_addr() failed\n");
+		return err;
+	}
+
+	/* First check if firmware has any address configured by bootloader */
+	if (!is_zero_ether_addr(mac_addr)) {
+		memcpy(net_dev->dev_addr, mac_addr, net_dev->addr_len);
+	} else {
+		/* No MAC address configured, fill in net_dev->dev_addr
+		 * with a random one
+		 */
+		eth_hw_addr_random(net_dev);
+		dev_dbg_once(dev, "device(s) have all-zero hwaddr, replaced with random\n");
+
+		/* Override NET_ADDR_RANDOM set by eth_hw_addr_random(); for all
+		 * practical purposes, this will be our "permanent" mac address,
+		 * at least until the next reboot. This move will also permit
+		 * register_netdevice() to properly fill up net_dev->perm_addr.
+		 */
+		net_dev->addr_assign_type = NET_ADDR_PERM;
+	}
+
+	return 0;
+}
+
+static const struct net_device_ops dpaa2_switch_port_ops = {
+	.ndo_open		= dpaa2_switch_port_open,
+	.ndo_stop		= dpaa2_switch_port_stop,
+
+	.ndo_set_mac_address	= eth_mac_addr,
+	.ndo_get_stats64	= dpaa2_switch_port_get_stats,
+	.ndo_change_mtu		= dpaa2_switch_port_change_mtu,
+	.ndo_has_offload_stats	= dpaa2_switch_port_has_offload_stats,
+	.ndo_get_offload_stats	= dpaa2_switch_port_get_offload_stats,
+	.ndo_fdb_add		= dpaa2_switch_port_fdb_add,
+	.ndo_fdb_del		= dpaa2_switch_port_fdb_del,
+	.ndo_fdb_dump		= dpaa2_switch_port_fdb_dump,
+
+	.ndo_start_xmit		= dpaa2_switch_port_dropframe,
+	.ndo_get_port_parent_id	= dpaa2_switch_port_parent_id,
+	.ndo_get_phys_port_name = dpaa2_switch_port_get_phys_name,
+};
+
+static bool dpaa2_switch_port_dev_check(const struct net_device *netdev,
+					struct notifier_block *nb)
+{
+	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
+
+	if (netdev->netdev_ops == &dpaa2_switch_port_ops &&
+	    (!nb || &port_priv->ethsw_data->port_nb == nb ||
+	     &port_priv->ethsw_data->port_switchdev_nb == nb ||
+	     &port_priv->ethsw_data->port_switchdevb_nb == nb))
+		return true;
+
+	return false;
+}
+
+static void dpaa2_switch_links_state_update(struct ethsw_core *ethsw)
 {
 	int i;
 
-	for (i = 0; i < ethsw->sw_attr.num_ifs; i++)
-		port_carrier_state_sync(ethsw->ports[i]->netdev);
+	for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
+		dpaa2_switch_port_carrier_state_sync(ethsw->ports[i]->netdev);
+		dpaa2_switch_port_set_mac_addr(ethsw->ports[i]);
+	}
 }
 
-static irqreturn_t ethsw_irq0_handler_thread(int irq_num, void *arg)
+static irqreturn_t dpaa2_switch_irq0_handler_thread(int irq_num, void *arg)
 {
 	struct device *dev = (struct device *)arg;
 	struct ethsw_core *ethsw = dev_get_drvdata(dev);
@@ -537,23 +809,23 @@
 	err = dpsw_get_irq_status(ethsw->mc_io, 0, ethsw->dpsw_handle,
 				  DPSW_IRQ_INDEX_IF, &status);
 	if (err) {
-		dev_err(dev, "Can't get irq status (err %d)", err);
+		dev_err(dev, "Can't get irq status (err %d)\n", err);
 
 		err = dpsw_clear_irq_status(ethsw->mc_io, 0, ethsw->dpsw_handle,
 					    DPSW_IRQ_INDEX_IF, 0xFFFFFFFF);
 		if (err)
-			dev_err(dev, "Can't clear irq status (err %d)", err);
+			dev_err(dev, "Can't clear irq status (err %d)\n", err);
 		goto out;
 	}
 
 	if (status & DPSW_IRQ_EVENT_LINK_CHANGED)
-		ethsw_links_state_update(ethsw);
+		dpaa2_switch_links_state_update(ethsw);
 
 out:
 	return IRQ_HANDLED;
 }
 
-static int ethsw_setup_irqs(struct fsl_mc_device *sw_dev)
+static int dpaa2_switch_setup_irqs(struct fsl_mc_device *sw_dev)
 {
 	struct device *dev = &sw_dev->dev;
 	struct ethsw_core *ethsw = dev_get_drvdata(dev);
@@ -583,25 +855,25 @@
 
 	err = devm_request_threaded_irq(dev, irq->msi_desc->irq,
 					NULL,
-					ethsw_irq0_handler_thread,
+					dpaa2_switch_irq0_handler_thread,
 					IRQF_NO_SUSPEND | IRQF_ONESHOT,
 					dev_name(dev), dev);
 	if (err) {
-		dev_err(dev, "devm_request_threaded_irq(): %d", err);
+		dev_err(dev, "devm_request_threaded_irq(): %d\n", err);
 		goto free_irq;
 	}
 
 	err = dpsw_set_irq_mask(ethsw->mc_io, 0, ethsw->dpsw_handle,
 				DPSW_IRQ_INDEX_IF, mask);
 	if (err) {
-		dev_err(dev, "dpsw_set_irq_mask(): %d", err);
+		dev_err(dev, "dpsw_set_irq_mask(): %d\n", err);
 		goto free_devm_irq;
 	}
 
 	err = dpsw_set_irq_enable(ethsw->mc_io, 0, ethsw->dpsw_handle,
 				  DPSW_IRQ_INDEX_IF, 1);
 	if (err) {
-		dev_err(dev, "dpsw_set_irq_enable(): %d", err);
+		dev_err(dev, "dpsw_set_irq_enable(): %d\n", err);
 		goto free_devm_irq;
 	}
 
@@ -614,7 +886,7 @@
 	return err;
 }
 
-static void ethsw_teardown_irqs(struct fsl_mc_device *sw_dev)
+static void dpaa2_switch_teardown_irqs(struct fsl_mc_device *sw_dev)
 {
 	struct device *dev = &sw_dev->dev;
 	struct ethsw_core *ethsw = dev_get_drvdata(dev);
@@ -628,46 +900,31 @@
 	fsl_mc_free_irqs(sw_dev);
 }
 
-static int swdev_port_attr_get(struct net_device *netdev,
-			       struct switchdev_attr *attr)
-{
-	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
-
-	switch (attr->id) {
-	case SWITCHDEV_ATTR_ID_PORT_PARENT_ID:
-		attr->u.ppid.id_len = 1;
-		attr->u.ppid.id[0] = port_priv->ethsw_data->dev_id;
-		break;
-	case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
-		attr->u.brport_flags =
-			(port_priv->ethsw_data->learning ? BR_LEARNING : 0) |
-			(port_priv->flood ? BR_FLOOD : 0);
-		break;
-	case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS_SUPPORT:
-		attr->u.brport_flags_support = BR_LEARNING | BR_FLOOD;
-		break;
-	default:
-		return -EOPNOTSUPP;
-	}
-
-	return 0;
-}
-
-static int port_attr_stp_state_set(struct net_device *netdev,
-				   struct switchdev_trans *trans,
-				   u8 state)
+static int dpaa2_switch_port_attr_stp_state_set(struct net_device *netdev,
+						struct switchdev_trans *trans,
+						u8 state)
 {
 	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
 
 	if (switchdev_trans_ph_prepare(trans))
 		return 0;
 
-	return ethsw_port_set_stp_state(port_priv, state);
+	return dpaa2_switch_port_set_stp_state(port_priv, state);
 }
 
-static int port_attr_br_flags_set(struct net_device *netdev,
-				  struct switchdev_trans *trans,
-				  unsigned long flags)
+static int dpaa2_switch_port_attr_br_flags_pre_set(struct net_device *netdev,
+						   struct switchdev_trans *trans,
+						   unsigned long flags)
+{
+	if (flags & ~(BR_LEARNING | BR_FLOOD))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int dpaa2_switch_port_attr_br_flags_set(struct net_device *netdev,
+					       struct switchdev_trans *trans,
+					       unsigned long flags)
 {
 	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
 	int err = 0;
@@ -676,30 +933,35 @@
 		return 0;
 
 	/* Learning is enabled per switch */
-	err = ethsw_set_learning(port_priv->ethsw_data, flags & BR_LEARNING);
+	err = dpaa2_switch_set_learning(port_priv->ethsw_data,
+					!!(flags & BR_LEARNING));
 	if (err)
 		goto exit;
 
-	err = ethsw_port_set_flood(port_priv, flags & BR_FLOOD);
+	err = dpaa2_switch_port_set_flood(port_priv, !!(flags & BR_FLOOD));
 
 exit:
 	return err;
 }
 
-static int swdev_port_attr_set(struct net_device *netdev,
-			       const struct switchdev_attr *attr,
-			       struct switchdev_trans *trans)
+static int dpaa2_switch_port_attr_set(struct net_device *netdev,
+				      const struct switchdev_attr *attr,
+				      struct switchdev_trans *trans)
 {
 	int err = 0;
 
 	switch (attr->id) {
 	case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
-		err = port_attr_stp_state_set(netdev, trans,
-					      attr->u.stp_state);
+		err = dpaa2_switch_port_attr_stp_state_set(netdev, trans,
+							   attr->u.stp_state);
+		break;
+	case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
+		err = dpaa2_switch_port_attr_br_flags_pre_set(netdev, trans,
+							      attr->u.brport_flags);
 		break;
 	case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
-		err = port_attr_br_flags_set(netdev, trans,
-					     attr->u.brport_flags);
+		err = dpaa2_switch_port_attr_br_flags_set(netdev, trans,
+							  attr->u.brport_flags);
 		break;
 	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
 		/* VLANs are supported by default  */
@@ -712,29 +974,43 @@
 	return err;
 }
 
-static int port_vlans_add(struct net_device *netdev,
-			  const struct switchdev_obj_port_vlan *vlan,
-			  struct switchdev_trans *trans)
+static int dpaa2_switch_port_vlans_add(struct net_device *netdev,
+				       const struct switchdev_obj_port_vlan *vlan,
+				       struct switchdev_trans *trans)
 {
 	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
-	int vid, err;
+	struct ethsw_core *ethsw = port_priv->ethsw_data;
+	struct dpsw_attr *attr = &ethsw->sw_attr;
+	int vid, err = 0, new_vlans = 0;
 
-	if (netif_is_bridge_master(vlan->obj.orig_dev))
-		return -EOPNOTSUPP;
+	if (switchdev_trans_ph_prepare(trans)) {
+		for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++)
+			if (!port_priv->ethsw_data->vlans[vid])
+				new_vlans++;
 
-	if (switchdev_trans_ph_prepare(trans))
+		/* Check if there is space for a new VLAN */
+		err = dpsw_get_attributes(ethsw->mc_io, 0, ethsw->dpsw_handle,
+					  &ethsw->sw_attr);
+		if (err) {
+			netdev_err(netdev, "dpsw_get_attributes err %d\n", err);
+			return err;
+		}
+		if (attr->max_vlans - attr->num_vlans < new_vlans)
+			return -ENOSPC;
+
 		return 0;
+	}
 
 	for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
 		if (!port_priv->ethsw_data->vlans[vid]) {
 			/* this is a new VLAN */
-			err = ethsw_add_vlan(port_priv->ethsw_data, vid);
+			err = dpaa2_switch_add_vlan(port_priv->ethsw_data, vid);
 			if (err)
 				return err;
 
 			port_priv->ethsw_data->vlans[vid] |= ETHSW_VLAN_GLOBAL;
 		}
-		err = ethsw_port_add_vlan(port_priv, vid, vlan->flags);
+		err = dpaa2_switch_port_add_vlan(port_priv, vid, vlan->flags);
 		if (err)
 			break;
 	}
@@ -742,8 +1018,8 @@
 	return err;
 }
 
-static int port_lookup_address(struct net_device *netdev, int is_uc,
-			       const unsigned char *addr)
+static int dpaa2_switch_port_lookup_address(struct net_device *netdev, int is_uc,
+					    const unsigned char *addr)
 {
 	struct netdev_hw_addr_list *list = (is_uc) ? &netdev->uc : &netdev->mc;
 	struct netdev_hw_addr *ha;
@@ -759,9 +1035,9 @@
 	return 0;
 }
 
-static int port_mdb_add(struct net_device *netdev,
-			const struct switchdev_obj_port_mdb *mdb,
-			struct switchdev_trans *trans)
+static int dpaa2_switch_port_mdb_add(struct net_device *netdev,
+				     const struct switchdev_obj_port_mdb *mdb,
+				     struct switchdev_trans *trans)
 {
 	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
 	int err;
@@ -770,38 +1046,38 @@
 		return 0;
 
 	/* Check if address is already set on this port */
-	if (port_lookup_address(netdev, 0, mdb->addr))
+	if (dpaa2_switch_port_lookup_address(netdev, 0, mdb->addr))
 		return -EEXIST;
 
-	err = ethsw_port_fdb_add_mc(port_priv, mdb->addr);
+	err = dpaa2_switch_port_fdb_add_mc(port_priv, mdb->addr);
 	if (err)
 		return err;
 
 	err = dev_mc_add(netdev, mdb->addr);
 	if (err) {
 		netdev_err(netdev, "dev_mc_add err %d\n", err);
-		ethsw_port_fdb_del_mc(port_priv, mdb->addr);
+		dpaa2_switch_port_fdb_del_mc(port_priv, mdb->addr);
 	}
 
 	return err;
 }
 
-static int swdev_port_obj_add(struct net_device *netdev,
-			      const struct switchdev_obj *obj,
-			      struct switchdev_trans *trans)
+static int dpaa2_switch_port_obj_add(struct net_device *netdev,
+				     const struct switchdev_obj *obj,
+				     struct switchdev_trans *trans)
 {
 	int err;
 
 	switch (obj->id) {
 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
-		err = port_vlans_add(netdev,
-				     SWITCHDEV_OBJ_PORT_VLAN(obj),
-				     trans);
+		err = dpaa2_switch_port_vlans_add(netdev,
+						  SWITCHDEV_OBJ_PORT_VLAN(obj),
+						  trans);
 		break;
 	case SWITCHDEV_OBJ_ID_PORT_MDB:
-		err = port_mdb_add(netdev,
-				   SWITCHDEV_OBJ_PORT_MDB(obj),
-				   trans);
+		err = dpaa2_switch_port_mdb_add(netdev,
+						SWITCHDEV_OBJ_PORT_MDB(obj),
+						trans);
 		break;
 	default:
 		err = -EOPNOTSUPP;
@@ -811,7 +1087,7 @@
 	return err;
 }
 
-static int ethsw_port_del_vlan(struct ethsw_port_priv *port_priv, u16 vid)
+static int dpaa2_switch_port_del_vlan(struct ethsw_port_priv *port_priv, u16 vid)
 {
 	struct ethsw_core *ethsw = port_priv->ethsw_data;
 	struct net_device *netdev = port_priv->netdev;
@@ -822,7 +1098,7 @@
 		return -ENOENT;
 
 	if (port_priv->vlans[vid] & ETHSW_VLAN_PVID) {
-		err = ethsw_port_set_pvid(port_priv, 0);
+		err = dpaa2_switch_port_set_pvid(port_priv, 0);
 		if (err)
 			return err;
 	}
@@ -860,7 +1136,7 @@
 
 		ethsw->vlans[vid] &= ~ETHSW_VLAN_GLOBAL;
 
-		err = ethsw_dellink_switch(ethsw, vid);
+		err = dpaa2_switch_dellink(ethsw, vid);
 		if (err)
 			return err;
 	}
@@ -868,17 +1144,17 @@
 	return 0;
 }
 
-static int port_vlans_del(struct net_device *netdev,
-			  const struct switchdev_obj_port_vlan *vlan)
+static int dpaa2_switch_port_vlans_del(struct net_device *netdev,
+				       const struct switchdev_obj_port_vlan *vlan)
 {
 	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
-	int vid, err;
+	int vid, err = 0;
 
 	if (netif_is_bridge_master(vlan->obj.orig_dev))
 		return -EOPNOTSUPP;
 
 	for (vid = vlan->vid_begin; vid <= vlan->vid_end; vid++) {
-		err = ethsw_port_del_vlan(port_priv, vid);
+		err = dpaa2_switch_port_del_vlan(port_priv, vid);
 		if (err)
 			break;
 	}
@@ -886,16 +1162,16 @@
 	return err;
 }
 
-static int port_mdb_del(struct net_device *netdev,
-			const struct switchdev_obj_port_mdb *mdb)
+static int dpaa2_switch_port_mdb_del(struct net_device *netdev,
+				     const struct switchdev_obj_port_mdb *mdb)
 {
 	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
 	int err;
 
-	if (!port_lookup_address(netdev, 0, mdb->addr))
+	if (!dpaa2_switch_port_lookup_address(netdev, 0, mdb->addr))
 		return -ENOENT;
 
-	err = ethsw_port_fdb_del_mc(port_priv, mdb->addr);
+	err = dpaa2_switch_port_fdb_del_mc(port_priv, mdb->addr);
 	if (err)
 		return err;
 
@@ -908,17 +1184,17 @@
 	return err;
 }
 
-static int swdev_port_obj_del(struct net_device *netdev,
-			      const struct switchdev_obj *obj)
+static int dpaa2_switch_port_obj_del(struct net_device *netdev,
+				     const struct switchdev_obj *obj)
 {
 	int err;
 
 	switch (obj->id) {
 	case SWITCHDEV_OBJ_ID_PORT_VLAN:
-		err = port_vlans_del(netdev, SWITCHDEV_OBJ_PORT_VLAN(obj));
+		err = dpaa2_switch_port_vlans_del(netdev, SWITCHDEV_OBJ_PORT_VLAN(obj));
 		break;
 	case SWITCHDEV_OBJ_ID_PORT_MDB:
-		err = port_mdb_del(netdev, SWITCHDEV_OBJ_PORT_MDB(obj));
+		err = dpaa2_switch_port_mdb_del(netdev, SWITCHDEV_OBJ_PORT_MDB(obj));
 		break;
 	default:
 		err = -EOPNOTSUPP;
@@ -927,60 +1203,80 @@
 	return err;
 }
 
-static const struct switchdev_ops ethsw_port_switchdev_ops = {
-	.switchdev_port_attr_get	= swdev_port_attr_get,
-	.switchdev_port_attr_set	= swdev_port_attr_set,
-	.switchdev_port_obj_add		= swdev_port_obj_add,
-	.switchdev_port_obj_del		= swdev_port_obj_del,
-};
+static int dpaa2_switch_port_attr_set_event(struct net_device *netdev,
+					    struct switchdev_notifier_port_attr_info
+					    *port_attr_info)
+{
+	int err;
+
+	err = dpaa2_switch_port_attr_set(netdev, port_attr_info->attr,
+					 port_attr_info->trans);
+
+	port_attr_info->handled = true;
+	return notifier_from_errno(err);
+}
 
 /* For the moment, only flood setting needs to be updated */
-static int port_bridge_join(struct net_device *netdev,
-			    struct net_device *upper_dev)
+static int dpaa2_switch_port_bridge_join(struct net_device *netdev,
+					 struct net_device *upper_dev)
 {
 	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
 	struct ethsw_core *ethsw = port_priv->ethsw_data;
+	struct ethsw_port_priv *other_port_priv;
+	struct net_device *other_dev;
+	struct list_head *iter;
 	int i, err;
 
 	for (i = 0; i < ethsw->sw_attr.num_ifs; i++)
 		if (ethsw->ports[i]->bridge_dev &&
 		    (ethsw->ports[i]->bridge_dev != upper_dev)) {
 			netdev_err(netdev,
-				   "Another switch port is connected to %s\n",
-				   ethsw->ports[i]->bridge_dev->name);
+				   "Only one bridge supported per DPSW object!\n");
 			return -EINVAL;
 		}
 
+	netdev_for_each_lower_dev(upper_dev, other_dev, iter) {
+		if (!dpaa2_switch_port_dev_check(other_dev, NULL))
+			continue;
+
+		other_port_priv = netdev_priv(other_dev);
+		if (other_port_priv->ethsw_data != port_priv->ethsw_data) {
+			netdev_err(netdev,
+				   "Interface from a different DPSW is in the bridge already!\n");
+			return -EINVAL;
+		}
+	}
+
 	/* Enable flooding */
-	err = ethsw_port_set_flood(port_priv, 1);
+	err = dpaa2_switch_port_set_flood(port_priv, 1);
 	if (!err)
 		port_priv->bridge_dev = upper_dev;
 
 	return err;
 }
 
-static int port_bridge_leave(struct net_device *netdev)
+static int dpaa2_switch_port_bridge_leave(struct net_device *netdev)
 {
 	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
 	int err;
 
 	/* Disable flooding */
-	err = ethsw_port_set_flood(port_priv, 0);
+	err = dpaa2_switch_port_set_flood(port_priv, 0);
 	if (!err)
 		port_priv->bridge_dev = NULL;
 
 	return err;
 }
 
-static int port_netdevice_event(struct notifier_block *unused,
-				unsigned long event, void *ptr)
+static int dpaa2_switch_port_netdevice_event(struct notifier_block *nb,
+					     unsigned long event, void *ptr)
 {
 	struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
 	struct netdev_notifier_changeupper_info *info = ptr;
 	struct net_device *upper_dev;
 	int err = 0;
 
-	if (netdev->netdev_ops != &ethsw_port_ops)
+	if (!dpaa2_switch_port_dev_check(netdev, nb))
 		return NOTIFY_DONE;
 
 	/* Handle just upper dev link/unlink for the moment */
@@ -988,18 +1284,14 @@
 		upper_dev = info->upper_dev;
 		if (netif_is_bridge_master(upper_dev)) {
 			if (info->linking)
-				err = port_bridge_join(netdev, upper_dev);
+				err = dpaa2_switch_port_bridge_join(netdev, upper_dev);
 			else
-				err = port_bridge_leave(netdev);
+				err = dpaa2_switch_port_bridge_leave(netdev);
 		}
 	}
 
 	return notifier_from_errno(err);
 }
-
-static struct notifier_block port_nb __read_mostly = {
-	.notifier_call = port_netdevice_event,
-};
 
 struct ethsw_switchdev_event_work {
 	struct work_struct work;
@@ -1008,30 +1300,40 @@
 	unsigned long event;
 };
 
-static void ethsw_switchdev_event_work(struct work_struct *work)
+static void dpaa2_switch_event_work(struct work_struct *work)
 {
 	struct ethsw_switchdev_event_work *switchdev_work =
 		container_of(work, struct ethsw_switchdev_event_work, work);
 	struct net_device *dev = switchdev_work->dev;
 	struct switchdev_notifier_fdb_info *fdb_info;
-	struct ethsw_port_priv *port_priv;
+	int err;
 
 	rtnl_lock();
-	port_priv = netdev_priv(dev);
 	fdb_info = &switchdev_work->fdb_info;
 
 	switch (switchdev_work->event) {
 	case SWITCHDEV_FDB_ADD_TO_DEVICE:
+		if (!fdb_info->added_by_user)
+			break;
 		if (is_unicast_ether_addr(fdb_info->addr))
-			ethsw_port_fdb_add_uc(netdev_priv(dev), fdb_info->addr);
+			err = dpaa2_switch_port_fdb_add_uc(netdev_priv(dev),
+							   fdb_info->addr);
 		else
-			ethsw_port_fdb_add_mc(netdev_priv(dev), fdb_info->addr);
+			err = dpaa2_switch_port_fdb_add_mc(netdev_priv(dev),
+							   fdb_info->addr);
+		if (err)
+			break;
+		fdb_info->offloaded = true;
+		call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, dev,
+					 &fdb_info->info, NULL);
 		break;
 	case SWITCHDEV_FDB_DEL_TO_DEVICE:
+		if (!fdb_info->added_by_user)
+			break;
 		if (is_unicast_ether_addr(fdb_info->addr))
-			ethsw_port_fdb_del_uc(netdev_priv(dev), fdb_info->addr);
+			dpaa2_switch_port_fdb_del_uc(netdev_priv(dev), fdb_info->addr);
 		else
-			ethsw_port_fdb_del_mc(netdev_priv(dev), fdb_info->addr);
+			dpaa2_switch_port_fdb_del_mc(netdev_priv(dev), fdb_info->addr);
 		break;
 	}
 
@@ -1042,18 +1344,26 @@
 }
 
 /* Called under rcu_read_lock() */
-static int port_switchdev_event(struct notifier_block *unused,
-				unsigned long event, void *ptr)
+static int dpaa2_switch_port_event(struct notifier_block *nb,
+				   unsigned long event, void *ptr)
 {
 	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
+	struct ethsw_port_priv *port_priv = netdev_priv(dev);
 	struct ethsw_switchdev_event_work *switchdev_work;
 	struct switchdev_notifier_fdb_info *fdb_info = ptr;
+	struct ethsw_core *ethsw = port_priv->ethsw_data;
+
+	if (!dpaa2_switch_port_dev_check(dev, nb))
+		return NOTIFY_DONE;
+
+	if (event == SWITCHDEV_PORT_ATTR_SET)
+		return dpaa2_switch_port_attr_set_event(dev, ptr);
 
 	switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
 	if (!switchdev_work)
 		return NOTIFY_BAD;
 
-	INIT_WORK(&switchdev_work->work, ethsw_switchdev_event_work);
+	INIT_WORK(&switchdev_work->work, dpaa2_switch_event_work);
 	switchdev_work->dev = dev;
 	switchdev_work->event = event;
 
@@ -1077,7 +1387,7 @@
 		return NOTIFY_DONE;
 	}
 
-	queue_work(ethsw_owq, &switchdev_work->work);
+	queue_work(ethsw->workqueue, &switchdev_work->work);
 
 	return NOTIFY_DONE;
 
@@ -1086,82 +1396,95 @@
 	return NOTIFY_BAD;
 }
 
-static struct notifier_block port_switchdev_nb = {
-	.notifier_call = port_switchdev_event,
-};
-
-static int ethsw_register_notifier(struct device *dev)
+static int dpaa2_switch_port_obj_event(unsigned long event,
+				       struct net_device *netdev,
+				       struct switchdev_notifier_port_obj_info *port_obj_info)
 {
+	int err = -EOPNOTSUPP;
+
+	switch (event) {
+	case SWITCHDEV_PORT_OBJ_ADD:
+		err = dpaa2_switch_port_obj_add(netdev, port_obj_info->obj,
+						port_obj_info->trans);
+		break;
+	case SWITCHDEV_PORT_OBJ_DEL:
+		err = dpaa2_switch_port_obj_del(netdev, port_obj_info->obj);
+		break;
+	}
+
+	port_obj_info->handled = true;
+	return notifier_from_errno(err);
+}
+
+static int dpaa2_switch_port_blocking_event(struct notifier_block *nb,
+					    unsigned long event, void *ptr)
+{
+	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
+
+	if (!dpaa2_switch_port_dev_check(dev, nb))
+		return NOTIFY_DONE;
+
+	switch (event) {
+	case SWITCHDEV_PORT_OBJ_ADD:
+	case SWITCHDEV_PORT_OBJ_DEL:
+		return dpaa2_switch_port_obj_event(event, dev, ptr);
+	case SWITCHDEV_PORT_ATTR_SET:
+		return dpaa2_switch_port_attr_set_event(dev, ptr);
+	}
+
+	return NOTIFY_DONE;
+}
+
+static int dpaa2_switch_register_notifier(struct device *dev)
+{
+	struct ethsw_core *ethsw = dev_get_drvdata(dev);
 	int err;
 
-	err = register_netdevice_notifier(&port_nb);
+	ethsw->port_nb.notifier_call = dpaa2_switch_port_netdevice_event;
+	err = register_netdevice_notifier(&ethsw->port_nb);
 	if (err) {
 		dev_err(dev, "Failed to register netdev notifier\n");
 		return err;
 	}
 
-	err = register_switchdev_notifier(&port_switchdev_nb);
+	ethsw->port_switchdev_nb.notifier_call = dpaa2_switch_port_event;
+	err = register_switchdev_notifier(&ethsw->port_switchdev_nb);
 	if (err) {
 		dev_err(dev, "Failed to register switchdev notifier\n");
 		goto err_switchdev_nb;
 	}
 
+	ethsw->port_switchdevb_nb.notifier_call = dpaa2_switch_port_blocking_event;
+	err = register_switchdev_blocking_notifier(&ethsw->port_switchdevb_nb);
+	if (err) {
+		dev_err(dev, "Failed to register switchdev blocking notifier\n");
+		goto err_switchdev_blocking_nb;
+	}
+
 	return 0;
 
+err_switchdev_blocking_nb:
+	unregister_switchdev_notifier(&ethsw->port_switchdev_nb);
 err_switchdev_nb:
-	unregister_netdevice_notifier(&port_nb);
+	unregister_netdevice_notifier(&ethsw->port_nb);
 	return err;
 }
 
-static int ethsw_open(struct ethsw_core *ethsw)
+static void dpaa2_switch_detect_features(struct ethsw_core *ethsw)
 {
-	struct ethsw_port_priv *port_priv = NULL;
-	int i, err;
+	ethsw->features = 0;
 
-	err = dpsw_enable(ethsw->mc_io, 0, ethsw->dpsw_handle);
-	if (err) {
-		dev_err(ethsw->dev, "dpsw_enable err %d\n", err);
-		return err;
-	}
-
-	for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
-		port_priv = ethsw->ports[i];
-		err = dev_open(port_priv->netdev);
-		if (err) {
-			netdev_err(port_priv->netdev, "dev_open err %d\n", err);
-			return err;
-		}
-	}
-
-	return 0;
+	if (ethsw->major > 8 || (ethsw->major == 8 && ethsw->minor >= 6))
+		ethsw->features |= ETHSW_FEATURE_MAC_ADDR;
 }
 
-static int ethsw_stop(struct ethsw_core *ethsw)
-{
-	struct ethsw_port_priv *port_priv = NULL;
-	int i, err;
-
-	for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
-		port_priv = ethsw->ports[i];
-		dev_close(port_priv->netdev);
-	}
-
-	err = dpsw_disable(ethsw->mc_io, 0, ethsw->dpsw_handle);
-	if (err) {
-		dev_err(ethsw->dev, "dpsw_disable err %d\n", err);
-		return err;
-	}
-
-	return 0;
-}
-
-static int ethsw_init(struct fsl_mc_device *sw_dev)
+static int dpaa2_switch_init(struct fsl_mc_device *sw_dev)
 {
 	struct device *dev = &sw_dev->dev;
 	struct ethsw_core *ethsw = dev_get_drvdata(dev);
-	u16 version_major, version_minor, i;
 	struct dpsw_stp_cfg stp_cfg;
 	int err;
+	u16 i;
 
 	ethsw->dev_id = sw_dev->obj_desc.id;
 
@@ -1179,24 +1502,26 @@
 	}
 
 	err = dpsw_get_api_version(ethsw->mc_io, 0,
-				   &version_major,
-				   &version_minor);
+				   &ethsw->major,
+				   &ethsw->minor);
 	if (err) {
 		dev_err(dev, "dpsw_get_api_version err %d\n", err);
 		goto err_close;
 	}
 
 	/* Minimum supported DPSW version check */
-	if (version_major < DPSW_MIN_VER_MAJOR ||
-	    (version_major == DPSW_MIN_VER_MAJOR &&
-	     version_minor < DPSW_MIN_VER_MINOR)) {
+	if (ethsw->major < DPSW_MIN_VER_MAJOR ||
+	    (ethsw->major == DPSW_MIN_VER_MAJOR &&
+	     ethsw->minor < DPSW_MIN_VER_MINOR)) {
 		dev_err(dev, "DPSW version %d:%d not supported. Use %d.%d or greater.\n",
-			version_major,
-			version_minor,
+			ethsw->major,
+			ethsw->minor,
 			DPSW_MIN_VER_MAJOR, DPSW_MIN_VER_MINOR);
 		err = -ENOTSUPP;
 		goto err_close;
 	}
+
+	dpaa2_switch_detect_features(ethsw);
 
 	err = dpsw_reset(ethsw->mc_io, 0, ethsw->dpsw_handle);
 	if (err) {
@@ -1233,30 +1558,30 @@
 		}
 	}
 
-	ethsw_owq = alloc_ordered_workqueue("%s_ordered", WQ_MEM_RECLAIM,
-					    "ethsw");
-	if (!ethsw_owq) {
+	ethsw->workqueue = alloc_ordered_workqueue("%s_%d_ordered",
+						   WQ_MEM_RECLAIM, "ethsw",
+						   ethsw->sw_attr.id);
+	if (!ethsw->workqueue) {
 		err = -ENOMEM;
 		goto err_close;
 	}
 
-	err = ethsw_register_notifier(dev);
+	err = dpaa2_switch_register_notifier(dev);
 	if (err)
 		goto err_destroy_ordered_workqueue;
 
 	return 0;
 
 err_destroy_ordered_workqueue:
-	destroy_workqueue(ethsw_owq);
+	destroy_workqueue(ethsw->workqueue);
 
 err_close:
 	dpsw_close(ethsw->mc_io, 0, ethsw->dpsw_handle);
 	return err;
 }
 
-static int ethsw_port_init(struct ethsw_port_priv *port_priv, u16 port)
+static int dpaa2_switch_port_init(struct ethsw_port_priv *port_priv, u16 port)
 {
-	const char def_mcast[ETH_ALEN] = {0x01, 0x00, 0x5e, 0x00, 0x00, 0x01};
 	struct net_device *netdev = port_priv->netdev;
 	struct ethsw_core *ethsw = port_priv->ethsw_data;
 	struct dpsw_vlan_if_cfg vcfg;
@@ -1276,51 +1601,56 @@
 		return err;
 	}
 
-	err = ethsw_port_set_pvid(port_priv, 0);
+	err = dpaa2_switch_port_set_pvid(port_priv, 0);
 	if (err)
 		return err;
 
 	err = dpsw_vlan_remove_if(ethsw->mc_io, 0, ethsw->dpsw_handle,
 				  DEFAULT_VLAN_ID, &vcfg);
-	if (err) {
+	if (err)
 		netdev_err(netdev, "dpsw_vlan_remove_if err %d\n", err);
-		return err;
-	}
-
-	err = ethsw_port_fdb_add_mc(port_priv, def_mcast);
 
 	return err;
 }
 
-static void ethsw_unregister_notifier(struct device *dev)
+static void dpaa2_switch_unregister_notifier(struct device *dev)
 {
+	struct ethsw_core *ethsw = dev_get_drvdata(dev);
+	struct notifier_block *nb;
 	int err;
 
-	err = unregister_switchdev_notifier(&port_switchdev_nb);
+	nb = &ethsw->port_switchdevb_nb;
+	err = unregister_switchdev_blocking_notifier(nb);
+	if (err)
+		dev_err(dev,
+			"Failed to unregister switchdev blocking notifier (%d)\n",
+			err);
+
+	err = unregister_switchdev_notifier(&ethsw->port_switchdev_nb);
 	if (err)
 		dev_err(dev,
 			"Failed to unregister switchdev notifier (%d)\n", err);
 
-	err = unregister_netdevice_notifier(&port_nb);
+	err = unregister_netdevice_notifier(&ethsw->port_nb);
 	if (err)
 		dev_err(dev,
 			"Failed to unregister netdev notifier (%d)\n", err);
 }
 
-static void ethsw_takedown(struct fsl_mc_device *sw_dev)
+static void dpaa2_switch_takedown(struct fsl_mc_device *sw_dev)
 {
 	struct device *dev = &sw_dev->dev;
 	struct ethsw_core *ethsw = dev_get_drvdata(dev);
 	int err;
 
-	ethsw_unregister_notifier(dev);
+	dpaa2_switch_unregister_notifier(dev);
 
 	err = dpsw_close(ethsw->mc_io, 0, ethsw->dpsw_handle);
 	if (err)
 		dev_warn(dev, "dpsw_close err %d\n", err);
 }
 
-static int ethsw_remove(struct fsl_mc_device *sw_dev)
+static int dpaa2_switch_remove(struct fsl_mc_device *sw_dev)
 {
 	struct ethsw_port_priv *port_priv;
 	struct ethsw_core *ethsw;
@@ -1330,13 +1660,9 @@
 	dev = &sw_dev->dev;
 	ethsw = dev_get_drvdata(dev);
 
-	ethsw_teardown_irqs(sw_dev);
+	dpaa2_switch_teardown_irqs(sw_dev);
 
-	destroy_workqueue(ethsw_owq);
-
-	rtnl_lock();
-	ethsw_stop(ethsw);
-	rtnl_unlock();
+	dpsw_disable(ethsw->mc_io, 0, ethsw->dpsw_handle);
 
 	for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
 		port_priv = ethsw->ports[i];
@@ -1345,7 +1671,10 @@
 	}
 	kfree(ethsw->ports);
 
-	ethsw_takedown(sw_dev);
+	dpaa2_switch_takedown(sw_dev);
+
+	destroy_workqueue(ethsw->workqueue);
+
 	fsl_mc_portal_free(ethsw->mc_io);
 
 	kfree(ethsw);
@@ -1355,7 +1684,8 @@
 	return 0;
 }
 
-static int ethsw_probe_port(struct ethsw_core *ethsw, u16 port_idx)
+static int dpaa2_switch_probe_port(struct ethsw_core *ethsw,
+				   u16 port_idx)
 {
 	struct ethsw_port_priv *port_priv;
 	struct device *dev = ethsw->dev;
@@ -1379,27 +1709,38 @@
 	port_priv->flood = true;
 
 	SET_NETDEV_DEV(port_netdev, dev);
-	port_netdev->netdev_ops = &ethsw_port_ops;
-	port_netdev->ethtool_ops = &ethsw_port_ethtool_ops;
-	port_netdev->switchdev_ops = &ethsw_port_switchdev_ops;
+	port_netdev->netdev_ops = &dpaa2_switch_port_ops;
+	port_netdev->ethtool_ops = &dpaa2_switch_port_ethtool_ops;
 
 	/* Set MTU limits */
 	port_netdev->min_mtu = ETH_MIN_MTU;
 	port_netdev->max_mtu = ETHSW_MAX_FRAME_LENGTH;
 
+	err = dpaa2_switch_port_init(port_priv, port_idx);
+	if (err)
+		goto err_port_probe;
+
+	err = dpaa2_switch_port_set_mac_addr(port_priv);
+	if (err)
+		goto err_port_probe;
+
 	err = register_netdev(port_netdev);
 	if (err < 0) {
 		dev_err(dev, "register_netdev error %d\n", err);
-		free_netdev(port_netdev);
-		return err;
+		goto err_port_probe;
 	}
 
 	ethsw->ports[port_idx] = port_priv;
 
-	return ethsw_port_init(port_priv, port_idx);
+	return 0;
+
+err_port_probe:
+	free_netdev(port_netdev);
+
+	return err;
 }
 
-static int ethsw_probe(struct fsl_mc_device *sw_dev)
+static int dpaa2_switch_probe(struct fsl_mc_device *sw_dev)
 {
 	struct device *dev = &sw_dev->dev;
 	struct ethsw_core *ethsw;
@@ -1414,7 +1755,8 @@
 	ethsw->dev = dev;
 	dev_set_drvdata(dev, ethsw);
 
-	err = fsl_mc_portal_allocate(sw_dev, 0, &ethsw->mc_io);
+	err = fsl_mc_portal_allocate(sw_dev, FSL_MC_IO_ATOMIC_CONTEXT_PORTAL,
+				     &ethsw->mc_io);
 	if (err) {
 		if (err == -ENXIO)
 			err = -EPROBE_DEFER;
@@ -1423,7 +1765,7 @@
 		goto err_free_drvdata;
 	}
 
-	err = ethsw_init(sw_dev);
+	err = dpaa2_switch_init(sw_dev);
 	if (err)
 		goto err_free_cmdport;
 
@@ -1441,20 +1783,23 @@
 	}
 
 	for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
-		err = ethsw_probe_port(ethsw, i);
+		err = dpaa2_switch_probe_port(ethsw, i);
 		if (err)
 			goto err_free_ports;
 	}
 
-	/* Switch starts up enabled */
-	rtnl_lock();
-	err = ethsw_open(ethsw);
-	rtnl_unlock();
-	if (err)
+	err = dpsw_enable(ethsw->mc_io, 0, ethsw->dpsw_handle);
+	if (err) {
+		dev_err(ethsw->dev, "dpsw_enable err %d\n", err);
 		goto err_free_ports;
+	}
+
+	/* Make sure the switch ports are disabled at probe time */
+	for (i = 0; i < ethsw->sw_attr.num_ifs; i++)
+		dpsw_if_disable(ethsw->mc_io, 0, ethsw->dpsw_handle, i);
 
 	/* Setup IRQs */
-	err = ethsw_setup_irqs(sw_dev);
+	err = dpaa2_switch_setup_irqs(sw_dev);
 	if (err)
 		goto err_stop;
 
@@ -1462,9 +1807,7 @@
 	return 0;
 
 err_stop:
-	rtnl_lock();
-	ethsw_stop(ethsw);
-	rtnl_unlock();
+	dpsw_disable(ethsw->mc_io, 0, ethsw->dpsw_handle);
 
 err_free_ports:
 	/* Cleanup registered ports only */
@@ -1475,7 +1818,7 @@
 	kfree(ethsw->ports);
 
 err_takedown:
-	ethsw_takedown(sw_dev);
+	dpaa2_switch_takedown(sw_dev);
 
 err_free_cmdport:
 	fsl_mc_portal_free(ethsw->mc_io);
@@ -1487,26 +1830,26 @@
 	return err;
 }
 
-static const struct fsl_mc_device_id ethsw_match_id_table[] = {
+static const struct fsl_mc_device_id dpaa2_switch_match_id_table[] = {
 	{
 		.vendor = FSL_MC_VENDOR_FREESCALE,
 		.obj_type = "dpsw",
 	},
 	{ .vendor = 0x0 }
 };
-MODULE_DEVICE_TABLE(fslmc, ethsw_match_id_table);
+MODULE_DEVICE_TABLE(fslmc, dpaa2_switch_match_id_table);
 
-static struct fsl_mc_driver eth_sw_drv = {
+static struct fsl_mc_driver dpaa2_switch_drv = {
 	.driver = {
 		.name = KBUILD_MODNAME,
 		.owner = THIS_MODULE,
 	},
-	.probe = ethsw_probe,
-	.remove = ethsw_remove,
-	.match_id_table = ethsw_match_id_table
+	.probe = dpaa2_switch_probe,
+	.remove = dpaa2_switch_remove,
+	.match_id_table = dpaa2_switch_match_id_table
 };
 
-module_fsl_mc_driver(eth_sw_drv);
+module_fsl_mc_driver(dpaa2_switch_drv);
 
 MODULE_LICENSE("GPL v2");
 MODULE_DESCRIPTION("DPAA2 Ethernet Switch Driver");

--
Gitblit v1.6.2