162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * DPAA2 Ethernet Switch driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright 2014-2016 Freescale Semiconductor Inc.
662306a36Sopenharmony_ci * Copyright 2017-2021 NXP
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/module.h>
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#include <linux/interrupt.h>
1362306a36Sopenharmony_ci#include <linux/kthread.h>
1462306a36Sopenharmony_ci#include <linux/workqueue.h>
1562306a36Sopenharmony_ci#include <linux/iommu.h>
1662306a36Sopenharmony_ci#include <net/pkt_cls.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <linux/fsl/mc.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#include "dpaa2-switch.h"
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci/* Minimal supported DPSW version */
2362306a36Sopenharmony_ci#define DPSW_MIN_VER_MAJOR		8
2462306a36Sopenharmony_ci#define DPSW_MIN_VER_MINOR		9
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci#define DEFAULT_VLAN_ID			1
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_cistatic u16 dpaa2_switch_port_get_fdb_id(struct ethsw_port_priv *port_priv)
2962306a36Sopenharmony_ci{
3062306a36Sopenharmony_ci	return port_priv->fdb->fdb_id;
3162306a36Sopenharmony_ci}
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic struct dpaa2_switch_fdb *dpaa2_switch_fdb_get_unused(struct ethsw_core *ethsw)
3462306a36Sopenharmony_ci{
3562306a36Sopenharmony_ci	int i;
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	for (i = 0; i < ethsw->sw_attr.num_ifs; i++)
3862306a36Sopenharmony_ci		if (!ethsw->fdbs[i].in_use)
3962306a36Sopenharmony_ci			return &ethsw->fdbs[i];
4062306a36Sopenharmony_ci	return NULL;
4162306a36Sopenharmony_ci}
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_cistatic struct dpaa2_switch_filter_block *
4462306a36Sopenharmony_cidpaa2_switch_filter_block_get_unused(struct ethsw_core *ethsw)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	int i;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	for (i = 0; i < ethsw->sw_attr.num_ifs; i++)
4962306a36Sopenharmony_ci		if (!ethsw->filter_blocks[i].in_use)
5062306a36Sopenharmony_ci			return &ethsw->filter_blocks[i];
5162306a36Sopenharmony_ci	return NULL;
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic u16 dpaa2_switch_port_set_fdb(struct ethsw_port_priv *port_priv,
5562306a36Sopenharmony_ci				     struct net_device *bridge_dev)
5662306a36Sopenharmony_ci{
5762306a36Sopenharmony_ci	struct ethsw_port_priv *other_port_priv = NULL;
5862306a36Sopenharmony_ci	struct dpaa2_switch_fdb *fdb;
5962306a36Sopenharmony_ci	struct net_device *other_dev;
6062306a36Sopenharmony_ci	struct list_head *iter;
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	/* If we leave a bridge (bridge_dev is NULL), find an unused
6362306a36Sopenharmony_ci	 * FDB and use that.
6462306a36Sopenharmony_ci	 */
6562306a36Sopenharmony_ci	if (!bridge_dev) {
6662306a36Sopenharmony_ci		fdb = dpaa2_switch_fdb_get_unused(port_priv->ethsw_data);
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci		/* If there is no unused FDB, we must be the last port that
6962306a36Sopenharmony_ci		 * leaves the last bridge, all the others are standalone. We
7062306a36Sopenharmony_ci		 * can just keep the FDB that we already have.
7162306a36Sopenharmony_ci		 */
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci		if (!fdb) {
7462306a36Sopenharmony_ci			port_priv->fdb->bridge_dev = NULL;
7562306a36Sopenharmony_ci			return 0;
7662306a36Sopenharmony_ci		}
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci		port_priv->fdb = fdb;
7962306a36Sopenharmony_ci		port_priv->fdb->in_use = true;
8062306a36Sopenharmony_ci		port_priv->fdb->bridge_dev = NULL;
8162306a36Sopenharmony_ci		return 0;
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	/* The below call to netdev_for_each_lower_dev() demands the RTNL lock
8562306a36Sopenharmony_ci	 * being held. Assert on it so that it's easier to catch new code
8662306a36Sopenharmony_ci	 * paths that reach this point without the RTNL lock.
8762306a36Sopenharmony_ci	 */
8862306a36Sopenharmony_ci	ASSERT_RTNL();
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	/* If part of a bridge, use the FDB of the first dpaa2 switch interface
9162306a36Sopenharmony_ci	 * to be present in that bridge
9262306a36Sopenharmony_ci	 */
9362306a36Sopenharmony_ci	netdev_for_each_lower_dev(bridge_dev, other_dev, iter) {
9462306a36Sopenharmony_ci		if (!dpaa2_switch_port_dev_check(other_dev))
9562306a36Sopenharmony_ci			continue;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci		if (other_dev == port_priv->netdev)
9862306a36Sopenharmony_ci			continue;
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci		other_port_priv = netdev_priv(other_dev);
10162306a36Sopenharmony_ci		break;
10262306a36Sopenharmony_ci	}
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	/* The current port is about to change its FDB to the one used by the
10562306a36Sopenharmony_ci	 * first port that joined the bridge.
10662306a36Sopenharmony_ci	 */
10762306a36Sopenharmony_ci	if (other_port_priv) {
10862306a36Sopenharmony_ci		/* The previous FDB is about to become unused, since the
10962306a36Sopenharmony_ci		 * interface is no longer standalone.
11062306a36Sopenharmony_ci		 */
11162306a36Sopenharmony_ci		port_priv->fdb->in_use = false;
11262306a36Sopenharmony_ci		port_priv->fdb->bridge_dev = NULL;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci		/* Get a reference to the new FDB */
11562306a36Sopenharmony_ci		port_priv->fdb = other_port_priv->fdb;
11662306a36Sopenharmony_ci	}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci	/* Keep track of the new upper bridge device */
11962306a36Sopenharmony_ci	port_priv->fdb->bridge_dev = bridge_dev;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	return 0;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic void dpaa2_switch_fdb_get_flood_cfg(struct ethsw_core *ethsw, u16 fdb_id,
12562306a36Sopenharmony_ci					   enum dpsw_flood_type type,
12662306a36Sopenharmony_ci					   struct dpsw_egress_flood_cfg *cfg)
12762306a36Sopenharmony_ci{
12862306a36Sopenharmony_ci	int i = 0, j;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	memset(cfg, 0, sizeof(*cfg));
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	/* Add all the DPAA2 switch ports found in the same bridging domain to
13362306a36Sopenharmony_ci	 * the egress flooding domain
13462306a36Sopenharmony_ci	 */
13562306a36Sopenharmony_ci	for (j = 0; j < ethsw->sw_attr.num_ifs; j++) {
13662306a36Sopenharmony_ci		if (!ethsw->ports[j])
13762306a36Sopenharmony_ci			continue;
13862306a36Sopenharmony_ci		if (ethsw->ports[j]->fdb->fdb_id != fdb_id)
13962306a36Sopenharmony_ci			continue;
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci		if (type == DPSW_BROADCAST && ethsw->ports[j]->bcast_flood)
14262306a36Sopenharmony_ci			cfg->if_id[i++] = ethsw->ports[j]->idx;
14362306a36Sopenharmony_ci		else if (type == DPSW_FLOODING && ethsw->ports[j]->ucast_flood)
14462306a36Sopenharmony_ci			cfg->if_id[i++] = ethsw->ports[j]->idx;
14562306a36Sopenharmony_ci	}
14662306a36Sopenharmony_ci
14762306a36Sopenharmony_ci	/* Add the CTRL interface to the egress flooding domain */
14862306a36Sopenharmony_ci	cfg->if_id[i++] = ethsw->sw_attr.num_ifs;
14962306a36Sopenharmony_ci
15062306a36Sopenharmony_ci	cfg->fdb_id = fdb_id;
15162306a36Sopenharmony_ci	cfg->flood_type = type;
15262306a36Sopenharmony_ci	cfg->num_ifs = i;
15362306a36Sopenharmony_ci}
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_cistatic int dpaa2_switch_fdb_set_egress_flood(struct ethsw_core *ethsw, u16 fdb_id)
15662306a36Sopenharmony_ci{
15762306a36Sopenharmony_ci	struct dpsw_egress_flood_cfg flood_cfg;
15862306a36Sopenharmony_ci	int err;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	/* Setup broadcast flooding domain */
16162306a36Sopenharmony_ci	dpaa2_switch_fdb_get_flood_cfg(ethsw, fdb_id, DPSW_BROADCAST, &flood_cfg);
16262306a36Sopenharmony_ci	err = dpsw_set_egress_flood(ethsw->mc_io, 0, ethsw->dpsw_handle,
16362306a36Sopenharmony_ci				    &flood_cfg);
16462306a36Sopenharmony_ci	if (err) {
16562306a36Sopenharmony_ci		dev_err(ethsw->dev, "dpsw_set_egress_flood() = %d\n", err);
16662306a36Sopenharmony_ci		return err;
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	/* Setup unknown flooding domain */
17062306a36Sopenharmony_ci	dpaa2_switch_fdb_get_flood_cfg(ethsw, fdb_id, DPSW_FLOODING, &flood_cfg);
17162306a36Sopenharmony_ci	err = dpsw_set_egress_flood(ethsw->mc_io, 0, ethsw->dpsw_handle,
17262306a36Sopenharmony_ci				    &flood_cfg);
17362306a36Sopenharmony_ci	if (err) {
17462306a36Sopenharmony_ci		dev_err(ethsw->dev, "dpsw_set_egress_flood() = %d\n", err);
17562306a36Sopenharmony_ci		return err;
17662306a36Sopenharmony_ci	}
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	return 0;
17962306a36Sopenharmony_ci}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_cistatic void *dpaa2_iova_to_virt(struct iommu_domain *domain,
18262306a36Sopenharmony_ci				dma_addr_t iova_addr)
18362306a36Sopenharmony_ci{
18462306a36Sopenharmony_ci	phys_addr_t phys_addr;
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	phys_addr = domain ? iommu_iova_to_phys(domain, iova_addr) : iova_addr;
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	return phys_to_virt(phys_addr);
18962306a36Sopenharmony_ci}
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_cistatic int dpaa2_switch_add_vlan(struct ethsw_port_priv *port_priv, u16 vid)
19262306a36Sopenharmony_ci{
19362306a36Sopenharmony_ci	struct ethsw_core *ethsw = port_priv->ethsw_data;
19462306a36Sopenharmony_ci	struct dpsw_vlan_cfg vcfg = {0};
19562306a36Sopenharmony_ci	int err;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	vcfg.fdb_id = dpaa2_switch_port_get_fdb_id(port_priv);
19862306a36Sopenharmony_ci	err = dpsw_vlan_add(ethsw->mc_io, 0,
19962306a36Sopenharmony_ci			    ethsw->dpsw_handle, vid, &vcfg);
20062306a36Sopenharmony_ci	if (err) {
20162306a36Sopenharmony_ci		dev_err(ethsw->dev, "dpsw_vlan_add err %d\n", err);
20262306a36Sopenharmony_ci		return err;
20362306a36Sopenharmony_ci	}
20462306a36Sopenharmony_ci	ethsw->vlans[vid] = ETHSW_VLAN_MEMBER;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	return 0;
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistatic bool dpaa2_switch_port_is_up(struct ethsw_port_priv *port_priv)
21062306a36Sopenharmony_ci{
21162306a36Sopenharmony_ci	struct net_device *netdev = port_priv->netdev;
21262306a36Sopenharmony_ci	struct dpsw_link_state state;
21362306a36Sopenharmony_ci	int err;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	err = dpsw_if_get_link_state(port_priv->ethsw_data->mc_io, 0,
21662306a36Sopenharmony_ci				     port_priv->ethsw_data->dpsw_handle,
21762306a36Sopenharmony_ci				     port_priv->idx, &state);
21862306a36Sopenharmony_ci	if (err) {
21962306a36Sopenharmony_ci		netdev_err(netdev, "dpsw_if_get_link_state() err %d\n", err);
22062306a36Sopenharmony_ci		return true;
22162306a36Sopenharmony_ci	}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_ci	WARN_ONCE(state.up > 1, "Garbage read into link_state");
22462306a36Sopenharmony_ci
22562306a36Sopenharmony_ci	return state.up ? true : false;
22662306a36Sopenharmony_ci}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_cistatic int dpaa2_switch_port_set_pvid(struct ethsw_port_priv *port_priv, u16 pvid)
22962306a36Sopenharmony_ci{
23062306a36Sopenharmony_ci	struct ethsw_core *ethsw = port_priv->ethsw_data;
23162306a36Sopenharmony_ci	struct net_device *netdev = port_priv->netdev;
23262306a36Sopenharmony_ci	struct dpsw_tci_cfg tci_cfg = { 0 };
23362306a36Sopenharmony_ci	bool up;
23462306a36Sopenharmony_ci	int err, ret;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	err = dpsw_if_get_tci(ethsw->mc_io, 0, ethsw->dpsw_handle,
23762306a36Sopenharmony_ci			      port_priv->idx, &tci_cfg);
23862306a36Sopenharmony_ci	if (err) {
23962306a36Sopenharmony_ci		netdev_err(netdev, "dpsw_if_get_tci err %d\n", err);
24062306a36Sopenharmony_ci		return err;
24162306a36Sopenharmony_ci	}
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	tci_cfg.vlan_id = pvid;
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	/* Interface needs to be down to change PVID */
24662306a36Sopenharmony_ci	up = dpaa2_switch_port_is_up(port_priv);
24762306a36Sopenharmony_ci	if (up) {
24862306a36Sopenharmony_ci		err = dpsw_if_disable(ethsw->mc_io, 0,
24962306a36Sopenharmony_ci				      ethsw->dpsw_handle,
25062306a36Sopenharmony_ci				      port_priv->idx);
25162306a36Sopenharmony_ci		if (err) {
25262306a36Sopenharmony_ci			netdev_err(netdev, "dpsw_if_disable err %d\n", err);
25362306a36Sopenharmony_ci			return err;
25462306a36Sopenharmony_ci		}
25562306a36Sopenharmony_ci	}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	err = dpsw_if_set_tci(ethsw->mc_io, 0, ethsw->dpsw_handle,
25862306a36Sopenharmony_ci			      port_priv->idx, &tci_cfg);
25962306a36Sopenharmony_ci	if (err) {
26062306a36Sopenharmony_ci		netdev_err(netdev, "dpsw_if_set_tci err %d\n", err);
26162306a36Sopenharmony_ci		goto set_tci_error;
26262306a36Sopenharmony_ci	}
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	/* Delete previous PVID info and mark the new one */
26562306a36Sopenharmony_ci	port_priv->vlans[port_priv->pvid] &= ~ETHSW_VLAN_PVID;
26662306a36Sopenharmony_ci	port_priv->vlans[pvid] |= ETHSW_VLAN_PVID;
26762306a36Sopenharmony_ci	port_priv->pvid = pvid;
26862306a36Sopenharmony_ci
26962306a36Sopenharmony_ciset_tci_error:
27062306a36Sopenharmony_ci	if (up) {
27162306a36Sopenharmony_ci		ret = dpsw_if_enable(ethsw->mc_io, 0,
27262306a36Sopenharmony_ci				     ethsw->dpsw_handle,
27362306a36Sopenharmony_ci				     port_priv->idx);
27462306a36Sopenharmony_ci		if (ret) {
27562306a36Sopenharmony_ci			netdev_err(netdev, "dpsw_if_enable err %d\n", ret);
27662306a36Sopenharmony_ci			return ret;
27762306a36Sopenharmony_ci		}
27862306a36Sopenharmony_ci	}
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	return err;
28162306a36Sopenharmony_ci}
28262306a36Sopenharmony_ci
28362306a36Sopenharmony_cistatic int dpaa2_switch_port_add_vlan(struct ethsw_port_priv *port_priv,
28462306a36Sopenharmony_ci				      u16 vid, u16 flags)
28562306a36Sopenharmony_ci{
28662306a36Sopenharmony_ci	struct ethsw_core *ethsw = port_priv->ethsw_data;
28762306a36Sopenharmony_ci	struct net_device *netdev = port_priv->netdev;
28862306a36Sopenharmony_ci	struct dpsw_vlan_if_cfg vcfg = {0};
28962306a36Sopenharmony_ci	int err;
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_ci	if (port_priv->vlans[vid]) {
29262306a36Sopenharmony_ci		netdev_warn(netdev, "VLAN %d already configured\n", vid);
29362306a36Sopenharmony_ci		return -EEXIST;
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	/* If hit, this VLAN rule will lead the packet into the FDB table
29762306a36Sopenharmony_ci	 * specified in the vlan configuration below
29862306a36Sopenharmony_ci	 */
29962306a36Sopenharmony_ci	vcfg.num_ifs = 1;
30062306a36Sopenharmony_ci	vcfg.if_id[0] = port_priv->idx;
30162306a36Sopenharmony_ci	vcfg.fdb_id = dpaa2_switch_port_get_fdb_id(port_priv);
30262306a36Sopenharmony_ci	vcfg.options |= DPSW_VLAN_ADD_IF_OPT_FDB_ID;
30362306a36Sopenharmony_ci	err = dpsw_vlan_add_if(ethsw->mc_io, 0, ethsw->dpsw_handle, vid, &vcfg);
30462306a36Sopenharmony_ci	if (err) {
30562306a36Sopenharmony_ci		netdev_err(netdev, "dpsw_vlan_add_if err %d\n", err);
30662306a36Sopenharmony_ci		return err;
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	port_priv->vlans[vid] = ETHSW_VLAN_MEMBER;
31062306a36Sopenharmony_ci
31162306a36Sopenharmony_ci	if (flags & BRIDGE_VLAN_INFO_UNTAGGED) {
31262306a36Sopenharmony_ci		err = dpsw_vlan_add_if_untagged(ethsw->mc_io, 0,
31362306a36Sopenharmony_ci						ethsw->dpsw_handle,
31462306a36Sopenharmony_ci						vid, &vcfg);
31562306a36Sopenharmony_ci		if (err) {
31662306a36Sopenharmony_ci			netdev_err(netdev,
31762306a36Sopenharmony_ci				   "dpsw_vlan_add_if_untagged err %d\n", err);
31862306a36Sopenharmony_ci			return err;
31962306a36Sopenharmony_ci		}
32062306a36Sopenharmony_ci		port_priv->vlans[vid] |= ETHSW_VLAN_UNTAGGED;
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	if (flags & BRIDGE_VLAN_INFO_PVID) {
32462306a36Sopenharmony_ci		err = dpaa2_switch_port_set_pvid(port_priv, vid);
32562306a36Sopenharmony_ci		if (err)
32662306a36Sopenharmony_ci			return err;
32762306a36Sopenharmony_ci	}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	return 0;
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ci
33262306a36Sopenharmony_cistatic enum dpsw_stp_state br_stp_state_to_dpsw(u8 state)
33362306a36Sopenharmony_ci{
33462306a36Sopenharmony_ci	switch (state) {
33562306a36Sopenharmony_ci	case BR_STATE_DISABLED:
33662306a36Sopenharmony_ci		return DPSW_STP_STATE_DISABLED;
33762306a36Sopenharmony_ci	case BR_STATE_LISTENING:
33862306a36Sopenharmony_ci		return DPSW_STP_STATE_LISTENING;
33962306a36Sopenharmony_ci	case BR_STATE_LEARNING:
34062306a36Sopenharmony_ci		return DPSW_STP_STATE_LEARNING;
34162306a36Sopenharmony_ci	case BR_STATE_FORWARDING:
34262306a36Sopenharmony_ci		return DPSW_STP_STATE_FORWARDING;
34362306a36Sopenharmony_ci	case BR_STATE_BLOCKING:
34462306a36Sopenharmony_ci		return DPSW_STP_STATE_BLOCKING;
34562306a36Sopenharmony_ci	default:
34662306a36Sopenharmony_ci		return DPSW_STP_STATE_DISABLED;
34762306a36Sopenharmony_ci	}
34862306a36Sopenharmony_ci}
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_cistatic int dpaa2_switch_port_set_stp_state(struct ethsw_port_priv *port_priv, u8 state)
35162306a36Sopenharmony_ci{
35262306a36Sopenharmony_ci	struct dpsw_stp_cfg stp_cfg = {0};
35362306a36Sopenharmony_ci	int err;
35462306a36Sopenharmony_ci	u16 vid;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	if (!netif_running(port_priv->netdev) || state == port_priv->stp_state)
35762306a36Sopenharmony_ci		return 0;	/* Nothing to do */
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_ci	stp_cfg.state = br_stp_state_to_dpsw(state);
36062306a36Sopenharmony_ci	for (vid = 0; vid <= VLAN_VID_MASK; vid++) {
36162306a36Sopenharmony_ci		if (port_priv->vlans[vid] & ETHSW_VLAN_MEMBER) {
36262306a36Sopenharmony_ci			stp_cfg.vlan_id = vid;
36362306a36Sopenharmony_ci			err = dpsw_if_set_stp(port_priv->ethsw_data->mc_io, 0,
36462306a36Sopenharmony_ci					      port_priv->ethsw_data->dpsw_handle,
36562306a36Sopenharmony_ci					      port_priv->idx, &stp_cfg);
36662306a36Sopenharmony_ci			if (err) {
36762306a36Sopenharmony_ci				netdev_err(port_priv->netdev,
36862306a36Sopenharmony_ci					   "dpsw_if_set_stp err %d\n", err);
36962306a36Sopenharmony_ci				return err;
37062306a36Sopenharmony_ci			}
37162306a36Sopenharmony_ci		}
37262306a36Sopenharmony_ci	}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	port_priv->stp_state = state;
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	return 0;
37762306a36Sopenharmony_ci}
37862306a36Sopenharmony_ci
37962306a36Sopenharmony_cistatic int dpaa2_switch_dellink(struct ethsw_core *ethsw, u16 vid)
38062306a36Sopenharmony_ci{
38162306a36Sopenharmony_ci	struct ethsw_port_priv *ppriv_local = NULL;
38262306a36Sopenharmony_ci	int i, err;
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	if (!ethsw->vlans[vid])
38562306a36Sopenharmony_ci		return -ENOENT;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	err = dpsw_vlan_remove(ethsw->mc_io, 0, ethsw->dpsw_handle, vid);
38862306a36Sopenharmony_ci	if (err) {
38962306a36Sopenharmony_ci		dev_err(ethsw->dev, "dpsw_vlan_remove err %d\n", err);
39062306a36Sopenharmony_ci		return err;
39162306a36Sopenharmony_ci	}
39262306a36Sopenharmony_ci	ethsw->vlans[vid] = 0;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
39562306a36Sopenharmony_ci		ppriv_local = ethsw->ports[i];
39662306a36Sopenharmony_ci		if (ppriv_local)
39762306a36Sopenharmony_ci			ppriv_local->vlans[vid] = 0;
39862306a36Sopenharmony_ci	}
39962306a36Sopenharmony_ci
40062306a36Sopenharmony_ci	return 0;
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ci
40362306a36Sopenharmony_cistatic int dpaa2_switch_port_fdb_add_uc(struct ethsw_port_priv *port_priv,
40462306a36Sopenharmony_ci					const unsigned char *addr)
40562306a36Sopenharmony_ci{
40662306a36Sopenharmony_ci	struct dpsw_fdb_unicast_cfg entry = {0};
40762306a36Sopenharmony_ci	u16 fdb_id;
40862306a36Sopenharmony_ci	int err;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	entry.if_egress = port_priv->idx;
41162306a36Sopenharmony_ci	entry.type = DPSW_FDB_ENTRY_STATIC;
41262306a36Sopenharmony_ci	ether_addr_copy(entry.mac_addr, addr);
41362306a36Sopenharmony_ci
41462306a36Sopenharmony_ci	fdb_id = dpaa2_switch_port_get_fdb_id(port_priv);
41562306a36Sopenharmony_ci	err = dpsw_fdb_add_unicast(port_priv->ethsw_data->mc_io, 0,
41662306a36Sopenharmony_ci				   port_priv->ethsw_data->dpsw_handle,
41762306a36Sopenharmony_ci				   fdb_id, &entry);
41862306a36Sopenharmony_ci	if (err)
41962306a36Sopenharmony_ci		netdev_err(port_priv->netdev,
42062306a36Sopenharmony_ci			   "dpsw_fdb_add_unicast err %d\n", err);
42162306a36Sopenharmony_ci	return err;
42262306a36Sopenharmony_ci}
42362306a36Sopenharmony_ci
42462306a36Sopenharmony_cistatic int dpaa2_switch_port_fdb_del_uc(struct ethsw_port_priv *port_priv,
42562306a36Sopenharmony_ci					const unsigned char *addr)
42662306a36Sopenharmony_ci{
42762306a36Sopenharmony_ci	struct dpsw_fdb_unicast_cfg entry = {0};
42862306a36Sopenharmony_ci	u16 fdb_id;
42962306a36Sopenharmony_ci	int err;
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	entry.if_egress = port_priv->idx;
43262306a36Sopenharmony_ci	entry.type = DPSW_FDB_ENTRY_STATIC;
43362306a36Sopenharmony_ci	ether_addr_copy(entry.mac_addr, addr);
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	fdb_id = dpaa2_switch_port_get_fdb_id(port_priv);
43662306a36Sopenharmony_ci	err = dpsw_fdb_remove_unicast(port_priv->ethsw_data->mc_io, 0,
43762306a36Sopenharmony_ci				      port_priv->ethsw_data->dpsw_handle,
43862306a36Sopenharmony_ci				      fdb_id, &entry);
43962306a36Sopenharmony_ci	/* Silently discard error for calling multiple times the del command */
44062306a36Sopenharmony_ci	if (err && err != -ENXIO)
44162306a36Sopenharmony_ci		netdev_err(port_priv->netdev,
44262306a36Sopenharmony_ci			   "dpsw_fdb_remove_unicast err %d\n", err);
44362306a36Sopenharmony_ci	return err;
44462306a36Sopenharmony_ci}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_cistatic int dpaa2_switch_port_fdb_add_mc(struct ethsw_port_priv *port_priv,
44762306a36Sopenharmony_ci					const unsigned char *addr)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	struct dpsw_fdb_multicast_cfg entry = {0};
45062306a36Sopenharmony_ci	u16 fdb_id;
45162306a36Sopenharmony_ci	int err;
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	ether_addr_copy(entry.mac_addr, addr);
45462306a36Sopenharmony_ci	entry.type = DPSW_FDB_ENTRY_STATIC;
45562306a36Sopenharmony_ci	entry.num_ifs = 1;
45662306a36Sopenharmony_ci	entry.if_id[0] = port_priv->idx;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	fdb_id = dpaa2_switch_port_get_fdb_id(port_priv);
45962306a36Sopenharmony_ci	err = dpsw_fdb_add_multicast(port_priv->ethsw_data->mc_io, 0,
46062306a36Sopenharmony_ci				     port_priv->ethsw_data->dpsw_handle,
46162306a36Sopenharmony_ci				     fdb_id, &entry);
46262306a36Sopenharmony_ci	/* Silently discard error for calling multiple times the add command */
46362306a36Sopenharmony_ci	if (err && err != -ENXIO)
46462306a36Sopenharmony_ci		netdev_err(port_priv->netdev, "dpsw_fdb_add_multicast err %d\n",
46562306a36Sopenharmony_ci			   err);
46662306a36Sopenharmony_ci	return err;
46762306a36Sopenharmony_ci}
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_cistatic int dpaa2_switch_port_fdb_del_mc(struct ethsw_port_priv *port_priv,
47062306a36Sopenharmony_ci					const unsigned char *addr)
47162306a36Sopenharmony_ci{
47262306a36Sopenharmony_ci	struct dpsw_fdb_multicast_cfg entry = {0};
47362306a36Sopenharmony_ci	u16 fdb_id;
47462306a36Sopenharmony_ci	int err;
47562306a36Sopenharmony_ci
47662306a36Sopenharmony_ci	ether_addr_copy(entry.mac_addr, addr);
47762306a36Sopenharmony_ci	entry.type = DPSW_FDB_ENTRY_STATIC;
47862306a36Sopenharmony_ci	entry.num_ifs = 1;
47962306a36Sopenharmony_ci	entry.if_id[0] = port_priv->idx;
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	fdb_id = dpaa2_switch_port_get_fdb_id(port_priv);
48262306a36Sopenharmony_ci	err = dpsw_fdb_remove_multicast(port_priv->ethsw_data->mc_io, 0,
48362306a36Sopenharmony_ci					port_priv->ethsw_data->dpsw_handle,
48462306a36Sopenharmony_ci					fdb_id, &entry);
48562306a36Sopenharmony_ci	/* Silently discard error for calling multiple times the del command */
48662306a36Sopenharmony_ci	if (err && err != -ENAVAIL)
48762306a36Sopenharmony_ci		netdev_err(port_priv->netdev,
48862306a36Sopenharmony_ci			   "dpsw_fdb_remove_multicast err %d\n", err);
48962306a36Sopenharmony_ci	return err;
49062306a36Sopenharmony_ci}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_cistatic void dpaa2_switch_port_get_stats(struct net_device *netdev,
49362306a36Sopenharmony_ci					struct rtnl_link_stats64 *stats)
49462306a36Sopenharmony_ci{
49562306a36Sopenharmony_ci	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
49662306a36Sopenharmony_ci	u64 tmp;
49762306a36Sopenharmony_ci	int err;
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
50062306a36Sopenharmony_ci				  port_priv->ethsw_data->dpsw_handle,
50162306a36Sopenharmony_ci				  port_priv->idx,
50262306a36Sopenharmony_ci				  DPSW_CNT_ING_FRAME, &stats->rx_packets);
50362306a36Sopenharmony_ci	if (err)
50462306a36Sopenharmony_ci		goto error;
50562306a36Sopenharmony_ci
50662306a36Sopenharmony_ci	err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
50762306a36Sopenharmony_ci				  port_priv->ethsw_data->dpsw_handle,
50862306a36Sopenharmony_ci				  port_priv->idx,
50962306a36Sopenharmony_ci				  DPSW_CNT_EGR_FRAME, &stats->tx_packets);
51062306a36Sopenharmony_ci	if (err)
51162306a36Sopenharmony_ci		goto error;
51262306a36Sopenharmony_ci
51362306a36Sopenharmony_ci	err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
51462306a36Sopenharmony_ci				  port_priv->ethsw_data->dpsw_handle,
51562306a36Sopenharmony_ci				  port_priv->idx,
51662306a36Sopenharmony_ci				  DPSW_CNT_ING_BYTE, &stats->rx_bytes);
51762306a36Sopenharmony_ci	if (err)
51862306a36Sopenharmony_ci		goto error;
51962306a36Sopenharmony_ci
52062306a36Sopenharmony_ci	err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
52162306a36Sopenharmony_ci				  port_priv->ethsw_data->dpsw_handle,
52262306a36Sopenharmony_ci				  port_priv->idx,
52362306a36Sopenharmony_ci				  DPSW_CNT_EGR_BYTE, &stats->tx_bytes);
52462306a36Sopenharmony_ci	if (err)
52562306a36Sopenharmony_ci		goto error;
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci	err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
52862306a36Sopenharmony_ci				  port_priv->ethsw_data->dpsw_handle,
52962306a36Sopenharmony_ci				  port_priv->idx,
53062306a36Sopenharmony_ci				  DPSW_CNT_ING_FRAME_DISCARD,
53162306a36Sopenharmony_ci				  &stats->rx_dropped);
53262306a36Sopenharmony_ci	if (err)
53362306a36Sopenharmony_ci		goto error;
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
53662306a36Sopenharmony_ci				  port_priv->ethsw_data->dpsw_handle,
53762306a36Sopenharmony_ci				  port_priv->idx,
53862306a36Sopenharmony_ci				  DPSW_CNT_ING_FLTR_FRAME,
53962306a36Sopenharmony_ci				  &tmp);
54062306a36Sopenharmony_ci	if (err)
54162306a36Sopenharmony_ci		goto error;
54262306a36Sopenharmony_ci	stats->rx_dropped += tmp;
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_ci	err = dpsw_if_get_counter(port_priv->ethsw_data->mc_io, 0,
54562306a36Sopenharmony_ci				  port_priv->ethsw_data->dpsw_handle,
54662306a36Sopenharmony_ci				  port_priv->idx,
54762306a36Sopenharmony_ci				  DPSW_CNT_EGR_FRAME_DISCARD,
54862306a36Sopenharmony_ci				  &stats->tx_dropped);
54962306a36Sopenharmony_ci	if (err)
55062306a36Sopenharmony_ci		goto error;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	return;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_cierror:
55562306a36Sopenharmony_ci	netdev_err(netdev, "dpsw_if_get_counter err %d\n", err);
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_cistatic bool dpaa2_switch_port_has_offload_stats(const struct net_device *netdev,
55962306a36Sopenharmony_ci						int attr_id)
56062306a36Sopenharmony_ci{
56162306a36Sopenharmony_ci	return (attr_id == IFLA_OFFLOAD_XSTATS_CPU_HIT);
56262306a36Sopenharmony_ci}
56362306a36Sopenharmony_ci
56462306a36Sopenharmony_cistatic int dpaa2_switch_port_get_offload_stats(int attr_id,
56562306a36Sopenharmony_ci					       const struct net_device *netdev,
56662306a36Sopenharmony_ci					       void *sp)
56762306a36Sopenharmony_ci{
56862306a36Sopenharmony_ci	switch (attr_id) {
56962306a36Sopenharmony_ci	case IFLA_OFFLOAD_XSTATS_CPU_HIT:
57062306a36Sopenharmony_ci		dpaa2_switch_port_get_stats((struct net_device *)netdev, sp);
57162306a36Sopenharmony_ci		return 0;
57262306a36Sopenharmony_ci	}
57362306a36Sopenharmony_ci
57462306a36Sopenharmony_ci	return -EINVAL;
57562306a36Sopenharmony_ci}
57662306a36Sopenharmony_ci
57762306a36Sopenharmony_cistatic int dpaa2_switch_port_change_mtu(struct net_device *netdev, int mtu)
57862306a36Sopenharmony_ci{
57962306a36Sopenharmony_ci	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
58062306a36Sopenharmony_ci	int err;
58162306a36Sopenharmony_ci
58262306a36Sopenharmony_ci	err = dpsw_if_set_max_frame_length(port_priv->ethsw_data->mc_io,
58362306a36Sopenharmony_ci					   0,
58462306a36Sopenharmony_ci					   port_priv->ethsw_data->dpsw_handle,
58562306a36Sopenharmony_ci					   port_priv->idx,
58662306a36Sopenharmony_ci					   (u16)ETHSW_L2_MAX_FRM(mtu));
58762306a36Sopenharmony_ci	if (err) {
58862306a36Sopenharmony_ci		netdev_err(netdev,
58962306a36Sopenharmony_ci			   "dpsw_if_set_max_frame_length() err %d\n", err);
59062306a36Sopenharmony_ci		return err;
59162306a36Sopenharmony_ci	}
59262306a36Sopenharmony_ci
59362306a36Sopenharmony_ci	netdev->mtu = mtu;
59462306a36Sopenharmony_ci	return 0;
59562306a36Sopenharmony_ci}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_cistatic int dpaa2_switch_port_link_state_update(struct net_device *netdev)
59862306a36Sopenharmony_ci{
59962306a36Sopenharmony_ci	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
60062306a36Sopenharmony_ci	struct dpsw_link_state state;
60162306a36Sopenharmony_ci	int err;
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	/* When we manage the MAC/PHY using phylink there is no need
60462306a36Sopenharmony_ci	 * to manually update the netif_carrier.
60562306a36Sopenharmony_ci	 * We can avoid locking because we are called from the "link changed"
60662306a36Sopenharmony_ci	 * IRQ handler, which is the same as the "endpoint changed" IRQ handler
60762306a36Sopenharmony_ci	 * (the writer to port_priv->mac), so we cannot race with it.
60862306a36Sopenharmony_ci	 */
60962306a36Sopenharmony_ci	if (dpaa2_mac_is_type_phy(port_priv->mac))
61062306a36Sopenharmony_ci		return 0;
61162306a36Sopenharmony_ci
61262306a36Sopenharmony_ci	/* Interrupts are received even though no one issued an 'ifconfig up'
61362306a36Sopenharmony_ci	 * on the switch interface. Ignore these link state update interrupts
61462306a36Sopenharmony_ci	 */
61562306a36Sopenharmony_ci	if (!netif_running(netdev))
61662306a36Sopenharmony_ci		return 0;
61762306a36Sopenharmony_ci
61862306a36Sopenharmony_ci	err = dpsw_if_get_link_state(port_priv->ethsw_data->mc_io, 0,
61962306a36Sopenharmony_ci				     port_priv->ethsw_data->dpsw_handle,
62062306a36Sopenharmony_ci				     port_priv->idx, &state);
62162306a36Sopenharmony_ci	if (err) {
62262306a36Sopenharmony_ci		netdev_err(netdev, "dpsw_if_get_link_state() err %d\n", err);
62362306a36Sopenharmony_ci		return err;
62462306a36Sopenharmony_ci	}
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci	WARN_ONCE(state.up > 1, "Garbage read into link_state");
62762306a36Sopenharmony_ci
62862306a36Sopenharmony_ci	if (state.up != port_priv->link_state) {
62962306a36Sopenharmony_ci		if (state.up) {
63062306a36Sopenharmony_ci			netif_carrier_on(netdev);
63162306a36Sopenharmony_ci			netif_tx_start_all_queues(netdev);
63262306a36Sopenharmony_ci		} else {
63362306a36Sopenharmony_ci			netif_carrier_off(netdev);
63462306a36Sopenharmony_ci			netif_tx_stop_all_queues(netdev);
63562306a36Sopenharmony_ci		}
63662306a36Sopenharmony_ci		port_priv->link_state = state.up;
63762306a36Sopenharmony_ci	}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	return 0;
64062306a36Sopenharmony_ci}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_ci/* Manage all NAPI instances for the control interface.
64362306a36Sopenharmony_ci *
64462306a36Sopenharmony_ci * We only have one RX queue and one Tx Conf queue for all
64562306a36Sopenharmony_ci * switch ports. Therefore, we only need to enable the NAPI instance once, the
64662306a36Sopenharmony_ci * first time one of the switch ports runs .dev_open().
64762306a36Sopenharmony_ci */
64862306a36Sopenharmony_ci
64962306a36Sopenharmony_cistatic void dpaa2_switch_enable_ctrl_if_napi(struct ethsw_core *ethsw)
65062306a36Sopenharmony_ci{
65162306a36Sopenharmony_ci	int i;
65262306a36Sopenharmony_ci
65362306a36Sopenharmony_ci	/* Access to the ethsw->napi_users relies on the RTNL lock */
65462306a36Sopenharmony_ci	ASSERT_RTNL();
65562306a36Sopenharmony_ci
65662306a36Sopenharmony_ci	/* a new interface is using the NAPI instance */
65762306a36Sopenharmony_ci	ethsw->napi_users++;
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ci	/* if there is already a user of the instance, return */
66062306a36Sopenharmony_ci	if (ethsw->napi_users > 1)
66162306a36Sopenharmony_ci		return;
66262306a36Sopenharmony_ci
66362306a36Sopenharmony_ci	for (i = 0; i < DPAA2_SWITCH_RX_NUM_FQS; i++)
66462306a36Sopenharmony_ci		napi_enable(&ethsw->fq[i].napi);
66562306a36Sopenharmony_ci}
66662306a36Sopenharmony_ci
66762306a36Sopenharmony_cistatic void dpaa2_switch_disable_ctrl_if_napi(struct ethsw_core *ethsw)
66862306a36Sopenharmony_ci{
66962306a36Sopenharmony_ci	int i;
67062306a36Sopenharmony_ci
67162306a36Sopenharmony_ci	/* Access to the ethsw->napi_users relies on the RTNL lock */
67262306a36Sopenharmony_ci	ASSERT_RTNL();
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	/* If we are not the last interface using the NAPI, return */
67562306a36Sopenharmony_ci	ethsw->napi_users--;
67662306a36Sopenharmony_ci	if (ethsw->napi_users)
67762306a36Sopenharmony_ci		return;
67862306a36Sopenharmony_ci
67962306a36Sopenharmony_ci	for (i = 0; i < DPAA2_SWITCH_RX_NUM_FQS; i++)
68062306a36Sopenharmony_ci		napi_disable(&ethsw->fq[i].napi);
68162306a36Sopenharmony_ci}
68262306a36Sopenharmony_ci
68362306a36Sopenharmony_cistatic int dpaa2_switch_port_open(struct net_device *netdev)
68462306a36Sopenharmony_ci{
68562306a36Sopenharmony_ci	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
68662306a36Sopenharmony_ci	struct ethsw_core *ethsw = port_priv->ethsw_data;
68762306a36Sopenharmony_ci	int err;
68862306a36Sopenharmony_ci
68962306a36Sopenharmony_ci	mutex_lock(&port_priv->mac_lock);
69062306a36Sopenharmony_ci
69162306a36Sopenharmony_ci	if (!dpaa2_switch_port_is_type_phy(port_priv)) {
69262306a36Sopenharmony_ci		/* Explicitly set carrier off, otherwise
69362306a36Sopenharmony_ci		 * netif_carrier_ok() will return true and cause 'ip link show'
69462306a36Sopenharmony_ci		 * to report the LOWER_UP flag, even though the link
69562306a36Sopenharmony_ci		 * notification wasn't even received.
69662306a36Sopenharmony_ci		 */
69762306a36Sopenharmony_ci		netif_carrier_off(netdev);
69862306a36Sopenharmony_ci	}
69962306a36Sopenharmony_ci
70062306a36Sopenharmony_ci	err = dpsw_if_enable(port_priv->ethsw_data->mc_io, 0,
70162306a36Sopenharmony_ci			     port_priv->ethsw_data->dpsw_handle,
70262306a36Sopenharmony_ci			     port_priv->idx);
70362306a36Sopenharmony_ci	if (err) {
70462306a36Sopenharmony_ci		mutex_unlock(&port_priv->mac_lock);
70562306a36Sopenharmony_ci		netdev_err(netdev, "dpsw_if_enable err %d\n", err);
70662306a36Sopenharmony_ci		return err;
70762306a36Sopenharmony_ci	}
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	dpaa2_switch_enable_ctrl_if_napi(ethsw);
71062306a36Sopenharmony_ci
71162306a36Sopenharmony_ci	if (dpaa2_switch_port_is_type_phy(port_priv))
71262306a36Sopenharmony_ci		dpaa2_mac_start(port_priv->mac);
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_ci	mutex_unlock(&port_priv->mac_lock);
71562306a36Sopenharmony_ci
71662306a36Sopenharmony_ci	return 0;
71762306a36Sopenharmony_ci}
71862306a36Sopenharmony_ci
71962306a36Sopenharmony_cistatic int dpaa2_switch_port_stop(struct net_device *netdev)
72062306a36Sopenharmony_ci{
72162306a36Sopenharmony_ci	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
72262306a36Sopenharmony_ci	struct ethsw_core *ethsw = port_priv->ethsw_data;
72362306a36Sopenharmony_ci	int err;
72462306a36Sopenharmony_ci
72562306a36Sopenharmony_ci	mutex_lock(&port_priv->mac_lock);
72662306a36Sopenharmony_ci
72762306a36Sopenharmony_ci	if (dpaa2_switch_port_is_type_phy(port_priv)) {
72862306a36Sopenharmony_ci		dpaa2_mac_stop(port_priv->mac);
72962306a36Sopenharmony_ci	} else {
73062306a36Sopenharmony_ci		netif_tx_stop_all_queues(netdev);
73162306a36Sopenharmony_ci		netif_carrier_off(netdev);
73262306a36Sopenharmony_ci	}
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	mutex_unlock(&port_priv->mac_lock);
73562306a36Sopenharmony_ci
73662306a36Sopenharmony_ci	err = dpsw_if_disable(port_priv->ethsw_data->mc_io, 0,
73762306a36Sopenharmony_ci			      port_priv->ethsw_data->dpsw_handle,
73862306a36Sopenharmony_ci			      port_priv->idx);
73962306a36Sopenharmony_ci	if (err) {
74062306a36Sopenharmony_ci		netdev_err(netdev, "dpsw_if_disable err %d\n", err);
74162306a36Sopenharmony_ci		return err;
74262306a36Sopenharmony_ci	}
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ci	dpaa2_switch_disable_ctrl_if_napi(ethsw);
74562306a36Sopenharmony_ci
74662306a36Sopenharmony_ci	return 0;
74762306a36Sopenharmony_ci}
74862306a36Sopenharmony_ci
74962306a36Sopenharmony_cistatic int dpaa2_switch_port_parent_id(struct net_device *dev,
75062306a36Sopenharmony_ci				       struct netdev_phys_item_id *ppid)
75162306a36Sopenharmony_ci{
75262306a36Sopenharmony_ci	struct ethsw_port_priv *port_priv = netdev_priv(dev);
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci	ppid->id_len = 1;
75562306a36Sopenharmony_ci	ppid->id[0] = port_priv->ethsw_data->dev_id;
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	return 0;
75862306a36Sopenharmony_ci}
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_cistatic int dpaa2_switch_port_get_phys_name(struct net_device *netdev, char *name,
76162306a36Sopenharmony_ci					   size_t len)
76262306a36Sopenharmony_ci{
76362306a36Sopenharmony_ci	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
76462306a36Sopenharmony_ci	int err;
76562306a36Sopenharmony_ci
76662306a36Sopenharmony_ci	err = snprintf(name, len, "p%d", port_priv->idx);
76762306a36Sopenharmony_ci	if (err >= len)
76862306a36Sopenharmony_ci		return -EINVAL;
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ci	return 0;
77162306a36Sopenharmony_ci}
77262306a36Sopenharmony_ci
77362306a36Sopenharmony_cistruct ethsw_dump_ctx {
77462306a36Sopenharmony_ci	struct net_device *dev;
77562306a36Sopenharmony_ci	struct sk_buff *skb;
77662306a36Sopenharmony_ci	struct netlink_callback *cb;
77762306a36Sopenharmony_ci	int idx;
77862306a36Sopenharmony_ci};
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_cistatic int dpaa2_switch_fdb_dump_nl(struct fdb_dump_entry *entry,
78162306a36Sopenharmony_ci				    struct ethsw_dump_ctx *dump)
78262306a36Sopenharmony_ci{
78362306a36Sopenharmony_ci	int is_dynamic = entry->type & DPSW_FDB_ENTRY_DINAMIC;
78462306a36Sopenharmony_ci	u32 portid = NETLINK_CB(dump->cb->skb).portid;
78562306a36Sopenharmony_ci	u32 seq = dump->cb->nlh->nlmsg_seq;
78662306a36Sopenharmony_ci	struct nlmsghdr *nlh;
78762306a36Sopenharmony_ci	struct ndmsg *ndm;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	if (dump->idx < dump->cb->args[2])
79062306a36Sopenharmony_ci		goto skip;
79162306a36Sopenharmony_ci
79262306a36Sopenharmony_ci	nlh = nlmsg_put(dump->skb, portid, seq, RTM_NEWNEIGH,
79362306a36Sopenharmony_ci			sizeof(*ndm), NLM_F_MULTI);
79462306a36Sopenharmony_ci	if (!nlh)
79562306a36Sopenharmony_ci		return -EMSGSIZE;
79662306a36Sopenharmony_ci
79762306a36Sopenharmony_ci	ndm = nlmsg_data(nlh);
79862306a36Sopenharmony_ci	ndm->ndm_family  = AF_BRIDGE;
79962306a36Sopenharmony_ci	ndm->ndm_pad1    = 0;
80062306a36Sopenharmony_ci	ndm->ndm_pad2    = 0;
80162306a36Sopenharmony_ci	ndm->ndm_flags   = NTF_SELF;
80262306a36Sopenharmony_ci	ndm->ndm_type    = 0;
80362306a36Sopenharmony_ci	ndm->ndm_ifindex = dump->dev->ifindex;
80462306a36Sopenharmony_ci	ndm->ndm_state   = is_dynamic ? NUD_REACHABLE : NUD_NOARP;
80562306a36Sopenharmony_ci
80662306a36Sopenharmony_ci	if (nla_put(dump->skb, NDA_LLADDR, ETH_ALEN, entry->mac_addr))
80762306a36Sopenharmony_ci		goto nla_put_failure;
80862306a36Sopenharmony_ci
80962306a36Sopenharmony_ci	nlmsg_end(dump->skb, nlh);
81062306a36Sopenharmony_ci
81162306a36Sopenharmony_ciskip:
81262306a36Sopenharmony_ci	dump->idx++;
81362306a36Sopenharmony_ci	return 0;
81462306a36Sopenharmony_ci
81562306a36Sopenharmony_cinla_put_failure:
81662306a36Sopenharmony_ci	nlmsg_cancel(dump->skb, nlh);
81762306a36Sopenharmony_ci	return -EMSGSIZE;
81862306a36Sopenharmony_ci}
81962306a36Sopenharmony_ci
82062306a36Sopenharmony_cistatic int dpaa2_switch_port_fdb_valid_entry(struct fdb_dump_entry *entry,
82162306a36Sopenharmony_ci					     struct ethsw_port_priv *port_priv)
82262306a36Sopenharmony_ci{
82362306a36Sopenharmony_ci	int idx = port_priv->idx;
82462306a36Sopenharmony_ci	int valid;
82562306a36Sopenharmony_ci
82662306a36Sopenharmony_ci	if (entry->type & DPSW_FDB_ENTRY_TYPE_UNICAST)
82762306a36Sopenharmony_ci		valid = entry->if_info == port_priv->idx;
82862306a36Sopenharmony_ci	else
82962306a36Sopenharmony_ci		valid = entry->if_mask[idx / 8] & BIT(idx % 8);
83062306a36Sopenharmony_ci
83162306a36Sopenharmony_ci	return valid;
83262306a36Sopenharmony_ci}
83362306a36Sopenharmony_ci
83462306a36Sopenharmony_cistatic int dpaa2_switch_fdb_iterate(struct ethsw_port_priv *port_priv,
83562306a36Sopenharmony_ci				    dpaa2_switch_fdb_cb_t cb, void *data)
83662306a36Sopenharmony_ci{
83762306a36Sopenharmony_ci	struct net_device *net_dev = port_priv->netdev;
83862306a36Sopenharmony_ci	struct ethsw_core *ethsw = port_priv->ethsw_data;
83962306a36Sopenharmony_ci	struct device *dev = net_dev->dev.parent;
84062306a36Sopenharmony_ci	struct fdb_dump_entry *fdb_entries;
84162306a36Sopenharmony_ci	struct fdb_dump_entry fdb_entry;
84262306a36Sopenharmony_ci	dma_addr_t fdb_dump_iova;
84362306a36Sopenharmony_ci	u16 num_fdb_entries;
84462306a36Sopenharmony_ci	u32 fdb_dump_size;
84562306a36Sopenharmony_ci	int err = 0, i;
84662306a36Sopenharmony_ci	u8 *dma_mem;
84762306a36Sopenharmony_ci	u16 fdb_id;
84862306a36Sopenharmony_ci
84962306a36Sopenharmony_ci	fdb_dump_size = ethsw->sw_attr.max_fdb_entries * sizeof(fdb_entry);
85062306a36Sopenharmony_ci	dma_mem = kzalloc(fdb_dump_size, GFP_KERNEL);
85162306a36Sopenharmony_ci	if (!dma_mem)
85262306a36Sopenharmony_ci		return -ENOMEM;
85362306a36Sopenharmony_ci
85462306a36Sopenharmony_ci	fdb_dump_iova = dma_map_single(dev, dma_mem, fdb_dump_size,
85562306a36Sopenharmony_ci				       DMA_FROM_DEVICE);
85662306a36Sopenharmony_ci	if (dma_mapping_error(dev, fdb_dump_iova)) {
85762306a36Sopenharmony_ci		netdev_err(net_dev, "dma_map_single() failed\n");
85862306a36Sopenharmony_ci		err = -ENOMEM;
85962306a36Sopenharmony_ci		goto err_map;
86062306a36Sopenharmony_ci	}
86162306a36Sopenharmony_ci
86262306a36Sopenharmony_ci	fdb_id = dpaa2_switch_port_get_fdb_id(port_priv);
86362306a36Sopenharmony_ci	err = dpsw_fdb_dump(ethsw->mc_io, 0, ethsw->dpsw_handle, fdb_id,
86462306a36Sopenharmony_ci			    fdb_dump_iova, fdb_dump_size, &num_fdb_entries);
86562306a36Sopenharmony_ci	if (err) {
86662306a36Sopenharmony_ci		netdev_err(net_dev, "dpsw_fdb_dump() = %d\n", err);
86762306a36Sopenharmony_ci		goto err_dump;
86862306a36Sopenharmony_ci	}
86962306a36Sopenharmony_ci
87062306a36Sopenharmony_ci	dma_unmap_single(dev, fdb_dump_iova, fdb_dump_size, DMA_FROM_DEVICE);
87162306a36Sopenharmony_ci
87262306a36Sopenharmony_ci	fdb_entries = (struct fdb_dump_entry *)dma_mem;
87362306a36Sopenharmony_ci	for (i = 0; i < num_fdb_entries; i++) {
87462306a36Sopenharmony_ci		fdb_entry = fdb_entries[i];
87562306a36Sopenharmony_ci
87662306a36Sopenharmony_ci		err = cb(port_priv, &fdb_entry, data);
87762306a36Sopenharmony_ci		if (err)
87862306a36Sopenharmony_ci			goto end;
87962306a36Sopenharmony_ci	}
88062306a36Sopenharmony_ci
88162306a36Sopenharmony_ciend:
88262306a36Sopenharmony_ci	kfree(dma_mem);
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci	return 0;
88562306a36Sopenharmony_ci
88662306a36Sopenharmony_cierr_dump:
88762306a36Sopenharmony_ci	dma_unmap_single(dev, fdb_dump_iova, fdb_dump_size, DMA_TO_DEVICE);
88862306a36Sopenharmony_cierr_map:
88962306a36Sopenharmony_ci	kfree(dma_mem);
89062306a36Sopenharmony_ci	return err;
89162306a36Sopenharmony_ci}
89262306a36Sopenharmony_ci
89362306a36Sopenharmony_cistatic int dpaa2_switch_fdb_entry_dump(struct ethsw_port_priv *port_priv,
89462306a36Sopenharmony_ci				       struct fdb_dump_entry *fdb_entry,
89562306a36Sopenharmony_ci				       void *data)
89662306a36Sopenharmony_ci{
89762306a36Sopenharmony_ci	if (!dpaa2_switch_port_fdb_valid_entry(fdb_entry, port_priv))
89862306a36Sopenharmony_ci		return 0;
89962306a36Sopenharmony_ci
90062306a36Sopenharmony_ci	return dpaa2_switch_fdb_dump_nl(fdb_entry, data);
90162306a36Sopenharmony_ci}
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_cistatic int dpaa2_switch_port_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb,
90462306a36Sopenharmony_ci				      struct net_device *net_dev,
90562306a36Sopenharmony_ci				      struct net_device *filter_dev, int *idx)
90662306a36Sopenharmony_ci{
90762306a36Sopenharmony_ci	struct ethsw_port_priv *port_priv = netdev_priv(net_dev);
90862306a36Sopenharmony_ci	struct ethsw_dump_ctx dump = {
90962306a36Sopenharmony_ci		.dev = net_dev,
91062306a36Sopenharmony_ci		.skb = skb,
91162306a36Sopenharmony_ci		.cb = cb,
91262306a36Sopenharmony_ci		.idx = *idx,
91362306a36Sopenharmony_ci	};
91462306a36Sopenharmony_ci	int err;
91562306a36Sopenharmony_ci
91662306a36Sopenharmony_ci	err = dpaa2_switch_fdb_iterate(port_priv, dpaa2_switch_fdb_entry_dump, &dump);
91762306a36Sopenharmony_ci	*idx = dump.idx;
91862306a36Sopenharmony_ci
91962306a36Sopenharmony_ci	return err;
92062306a36Sopenharmony_ci}
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_cistatic int dpaa2_switch_fdb_entry_fast_age(struct ethsw_port_priv *port_priv,
92362306a36Sopenharmony_ci					   struct fdb_dump_entry *fdb_entry,
92462306a36Sopenharmony_ci					   void *data __always_unused)
92562306a36Sopenharmony_ci{
92662306a36Sopenharmony_ci	if (!dpaa2_switch_port_fdb_valid_entry(fdb_entry, port_priv))
92762306a36Sopenharmony_ci		return 0;
92862306a36Sopenharmony_ci
92962306a36Sopenharmony_ci	if (!(fdb_entry->type & DPSW_FDB_ENTRY_TYPE_DYNAMIC))
93062306a36Sopenharmony_ci		return 0;
93162306a36Sopenharmony_ci
93262306a36Sopenharmony_ci	if (fdb_entry->type & DPSW_FDB_ENTRY_TYPE_UNICAST)
93362306a36Sopenharmony_ci		dpaa2_switch_port_fdb_del_uc(port_priv, fdb_entry->mac_addr);
93462306a36Sopenharmony_ci	else
93562306a36Sopenharmony_ci		dpaa2_switch_port_fdb_del_mc(port_priv, fdb_entry->mac_addr);
93662306a36Sopenharmony_ci
93762306a36Sopenharmony_ci	return 0;
93862306a36Sopenharmony_ci}
93962306a36Sopenharmony_ci
94062306a36Sopenharmony_cistatic void dpaa2_switch_port_fast_age(struct ethsw_port_priv *port_priv)
94162306a36Sopenharmony_ci{
94262306a36Sopenharmony_ci	dpaa2_switch_fdb_iterate(port_priv,
94362306a36Sopenharmony_ci				 dpaa2_switch_fdb_entry_fast_age, NULL);
94462306a36Sopenharmony_ci}
94562306a36Sopenharmony_ci
94662306a36Sopenharmony_cistatic int dpaa2_switch_port_vlan_add(struct net_device *netdev, __be16 proto,
94762306a36Sopenharmony_ci				      u16 vid)
94862306a36Sopenharmony_ci{
94962306a36Sopenharmony_ci	struct switchdev_obj_port_vlan vlan = {
95062306a36Sopenharmony_ci		.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
95162306a36Sopenharmony_ci		.vid = vid,
95262306a36Sopenharmony_ci		.obj.orig_dev = netdev,
95362306a36Sopenharmony_ci		/* This API only allows programming tagged, non-PVID VIDs */
95462306a36Sopenharmony_ci		.flags = 0,
95562306a36Sopenharmony_ci	};
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	return dpaa2_switch_port_vlans_add(netdev, &vlan);
95862306a36Sopenharmony_ci}
95962306a36Sopenharmony_ci
96062306a36Sopenharmony_cistatic int dpaa2_switch_port_vlan_kill(struct net_device *netdev, __be16 proto,
96162306a36Sopenharmony_ci				       u16 vid)
96262306a36Sopenharmony_ci{
96362306a36Sopenharmony_ci	struct switchdev_obj_port_vlan vlan = {
96462306a36Sopenharmony_ci		.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
96562306a36Sopenharmony_ci		.vid = vid,
96662306a36Sopenharmony_ci		.obj.orig_dev = netdev,
96762306a36Sopenharmony_ci		/* This API only allows programming tagged, non-PVID VIDs */
96862306a36Sopenharmony_ci		.flags = 0,
96962306a36Sopenharmony_ci	};
97062306a36Sopenharmony_ci
97162306a36Sopenharmony_ci	return dpaa2_switch_port_vlans_del(netdev, &vlan);
97262306a36Sopenharmony_ci}
97362306a36Sopenharmony_ci
97462306a36Sopenharmony_cistatic int dpaa2_switch_port_set_mac_addr(struct ethsw_port_priv *port_priv)
97562306a36Sopenharmony_ci{
97662306a36Sopenharmony_ci	struct ethsw_core *ethsw = port_priv->ethsw_data;
97762306a36Sopenharmony_ci	struct net_device *net_dev = port_priv->netdev;
97862306a36Sopenharmony_ci	struct device *dev = net_dev->dev.parent;
97962306a36Sopenharmony_ci	u8 mac_addr[ETH_ALEN];
98062306a36Sopenharmony_ci	int err;
98162306a36Sopenharmony_ci
98262306a36Sopenharmony_ci	if (!(ethsw->features & ETHSW_FEATURE_MAC_ADDR))
98362306a36Sopenharmony_ci		return 0;
98462306a36Sopenharmony_ci
98562306a36Sopenharmony_ci	/* Get firmware address, if any */
98662306a36Sopenharmony_ci	err = dpsw_if_get_port_mac_addr(ethsw->mc_io, 0, ethsw->dpsw_handle,
98762306a36Sopenharmony_ci					port_priv->idx, mac_addr);
98862306a36Sopenharmony_ci	if (err) {
98962306a36Sopenharmony_ci		dev_err(dev, "dpsw_if_get_port_mac_addr() failed\n");
99062306a36Sopenharmony_ci		return err;
99162306a36Sopenharmony_ci	}
99262306a36Sopenharmony_ci
99362306a36Sopenharmony_ci	/* First check if firmware has any address configured by bootloader */
99462306a36Sopenharmony_ci	if (!is_zero_ether_addr(mac_addr)) {
99562306a36Sopenharmony_ci		eth_hw_addr_set(net_dev, mac_addr);
99662306a36Sopenharmony_ci	} else {
99762306a36Sopenharmony_ci		/* No MAC address configured, fill in net_dev->dev_addr
99862306a36Sopenharmony_ci		 * with a random one
99962306a36Sopenharmony_ci		 */
100062306a36Sopenharmony_ci		eth_hw_addr_random(net_dev);
100162306a36Sopenharmony_ci		dev_dbg_once(dev, "device(s) have all-zero hwaddr, replaced with random\n");
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci		/* Override NET_ADDR_RANDOM set by eth_hw_addr_random(); for all
100462306a36Sopenharmony_ci		 * practical purposes, this will be our "permanent" mac address,
100562306a36Sopenharmony_ci		 * at least until the next reboot. This move will also permit
100662306a36Sopenharmony_ci		 * register_netdevice() to properly fill up net_dev->perm_addr.
100762306a36Sopenharmony_ci		 */
100862306a36Sopenharmony_ci		net_dev->addr_assign_type = NET_ADDR_PERM;
100962306a36Sopenharmony_ci	}
101062306a36Sopenharmony_ci
101162306a36Sopenharmony_ci	return 0;
101262306a36Sopenharmony_ci}
101362306a36Sopenharmony_ci
101462306a36Sopenharmony_cistatic void dpaa2_switch_free_fd(const struct ethsw_core *ethsw,
101562306a36Sopenharmony_ci				 const struct dpaa2_fd *fd)
101662306a36Sopenharmony_ci{
101762306a36Sopenharmony_ci	struct device *dev = ethsw->dev;
101862306a36Sopenharmony_ci	unsigned char *buffer_start;
101962306a36Sopenharmony_ci	struct sk_buff **skbh, *skb;
102062306a36Sopenharmony_ci	dma_addr_t fd_addr;
102162306a36Sopenharmony_ci
102262306a36Sopenharmony_ci	fd_addr = dpaa2_fd_get_addr(fd);
102362306a36Sopenharmony_ci	skbh = dpaa2_iova_to_virt(ethsw->iommu_domain, fd_addr);
102462306a36Sopenharmony_ci
102562306a36Sopenharmony_ci	skb = *skbh;
102662306a36Sopenharmony_ci	buffer_start = (unsigned char *)skbh;
102762306a36Sopenharmony_ci
102862306a36Sopenharmony_ci	dma_unmap_single(dev, fd_addr,
102962306a36Sopenharmony_ci			 skb_tail_pointer(skb) - buffer_start,
103062306a36Sopenharmony_ci			 DMA_TO_DEVICE);
103162306a36Sopenharmony_ci
103262306a36Sopenharmony_ci	/* Move on with skb release */
103362306a36Sopenharmony_ci	dev_kfree_skb(skb);
103462306a36Sopenharmony_ci}
103562306a36Sopenharmony_ci
103662306a36Sopenharmony_cistatic int dpaa2_switch_build_single_fd(struct ethsw_core *ethsw,
103762306a36Sopenharmony_ci					struct sk_buff *skb,
103862306a36Sopenharmony_ci					struct dpaa2_fd *fd)
103962306a36Sopenharmony_ci{
104062306a36Sopenharmony_ci	struct device *dev = ethsw->dev;
104162306a36Sopenharmony_ci	struct sk_buff **skbh;
104262306a36Sopenharmony_ci	dma_addr_t addr;
104362306a36Sopenharmony_ci	u8 *buff_start;
104462306a36Sopenharmony_ci	void *hwa;
104562306a36Sopenharmony_ci
104662306a36Sopenharmony_ci	buff_start = PTR_ALIGN(skb->data - DPAA2_SWITCH_TX_DATA_OFFSET -
104762306a36Sopenharmony_ci			       DPAA2_SWITCH_TX_BUF_ALIGN,
104862306a36Sopenharmony_ci			       DPAA2_SWITCH_TX_BUF_ALIGN);
104962306a36Sopenharmony_ci
105062306a36Sopenharmony_ci	/* Clear FAS to have consistent values for TX confirmation. It is
105162306a36Sopenharmony_ci	 * located in the first 8 bytes of the buffer's hardware annotation
105262306a36Sopenharmony_ci	 * area
105362306a36Sopenharmony_ci	 */
105462306a36Sopenharmony_ci	hwa = buff_start + DPAA2_SWITCH_SWA_SIZE;
105562306a36Sopenharmony_ci	memset(hwa, 0, 8);
105662306a36Sopenharmony_ci
105762306a36Sopenharmony_ci	/* Store a backpointer to the skb at the beginning of the buffer
105862306a36Sopenharmony_ci	 * (in the private data area) such that we can release it
105962306a36Sopenharmony_ci	 * on Tx confirm
106062306a36Sopenharmony_ci	 */
106162306a36Sopenharmony_ci	skbh = (struct sk_buff **)buff_start;
106262306a36Sopenharmony_ci	*skbh = skb;
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	addr = dma_map_single(dev, buff_start,
106562306a36Sopenharmony_ci			      skb_tail_pointer(skb) - buff_start,
106662306a36Sopenharmony_ci			      DMA_TO_DEVICE);
106762306a36Sopenharmony_ci	if (unlikely(dma_mapping_error(dev, addr)))
106862306a36Sopenharmony_ci		return -ENOMEM;
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	/* Setup the FD fields */
107162306a36Sopenharmony_ci	memset(fd, 0, sizeof(*fd));
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ci	dpaa2_fd_set_addr(fd, addr);
107462306a36Sopenharmony_ci	dpaa2_fd_set_offset(fd, (u16)(skb->data - buff_start));
107562306a36Sopenharmony_ci	dpaa2_fd_set_len(fd, skb->len);
107662306a36Sopenharmony_ci	dpaa2_fd_set_format(fd, dpaa2_fd_single);
107762306a36Sopenharmony_ci
107862306a36Sopenharmony_ci	return 0;
107962306a36Sopenharmony_ci}
108062306a36Sopenharmony_ci
108162306a36Sopenharmony_cistatic netdev_tx_t dpaa2_switch_port_tx(struct sk_buff *skb,
108262306a36Sopenharmony_ci					struct net_device *net_dev)
108362306a36Sopenharmony_ci{
108462306a36Sopenharmony_ci	struct ethsw_port_priv *port_priv = netdev_priv(net_dev);
108562306a36Sopenharmony_ci	struct ethsw_core *ethsw = port_priv->ethsw_data;
108662306a36Sopenharmony_ci	int retries = DPAA2_SWITCH_SWP_BUSY_RETRIES;
108762306a36Sopenharmony_ci	struct dpaa2_fd fd;
108862306a36Sopenharmony_ci	int err;
108962306a36Sopenharmony_ci
109062306a36Sopenharmony_ci	if (unlikely(skb_headroom(skb) < DPAA2_SWITCH_NEEDED_HEADROOM)) {
109162306a36Sopenharmony_ci		struct sk_buff *ns;
109262306a36Sopenharmony_ci
109362306a36Sopenharmony_ci		ns = skb_realloc_headroom(skb, DPAA2_SWITCH_NEEDED_HEADROOM);
109462306a36Sopenharmony_ci		if (unlikely(!ns)) {
109562306a36Sopenharmony_ci			net_err_ratelimited("%s: Error reallocating skb headroom\n", net_dev->name);
109662306a36Sopenharmony_ci			goto err_free_skb;
109762306a36Sopenharmony_ci		}
109862306a36Sopenharmony_ci		dev_consume_skb_any(skb);
109962306a36Sopenharmony_ci		skb = ns;
110062306a36Sopenharmony_ci	}
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	/* We'll be holding a back-reference to the skb until Tx confirmation */
110362306a36Sopenharmony_ci	skb = skb_unshare(skb, GFP_ATOMIC);
110462306a36Sopenharmony_ci	if (unlikely(!skb)) {
110562306a36Sopenharmony_ci		/* skb_unshare() has already freed the skb */
110662306a36Sopenharmony_ci		net_err_ratelimited("%s: Error copying the socket buffer\n", net_dev->name);
110762306a36Sopenharmony_ci		goto err_exit;
110862306a36Sopenharmony_ci	}
110962306a36Sopenharmony_ci
111062306a36Sopenharmony_ci	/* At this stage, we do not support non-linear skbs so just try to
111162306a36Sopenharmony_ci	 * linearize the skb and if that's not working, just drop the packet.
111262306a36Sopenharmony_ci	 */
111362306a36Sopenharmony_ci	err = skb_linearize(skb);
111462306a36Sopenharmony_ci	if (err) {
111562306a36Sopenharmony_ci		net_err_ratelimited("%s: skb_linearize error (%d)!\n", net_dev->name, err);
111662306a36Sopenharmony_ci		goto err_free_skb;
111762306a36Sopenharmony_ci	}
111862306a36Sopenharmony_ci
111962306a36Sopenharmony_ci	err = dpaa2_switch_build_single_fd(ethsw, skb, &fd);
112062306a36Sopenharmony_ci	if (unlikely(err)) {
112162306a36Sopenharmony_ci		net_err_ratelimited("%s: ethsw_build_*_fd() %d\n", net_dev->name, err);
112262306a36Sopenharmony_ci		goto err_free_skb;
112362306a36Sopenharmony_ci	}
112462306a36Sopenharmony_ci
112562306a36Sopenharmony_ci	do {
112662306a36Sopenharmony_ci		err = dpaa2_io_service_enqueue_qd(NULL,
112762306a36Sopenharmony_ci						  port_priv->tx_qdid,
112862306a36Sopenharmony_ci						  8, 0, &fd);
112962306a36Sopenharmony_ci		retries--;
113062306a36Sopenharmony_ci	} while (err == -EBUSY && retries);
113162306a36Sopenharmony_ci
113262306a36Sopenharmony_ci	if (unlikely(err < 0)) {
113362306a36Sopenharmony_ci		dpaa2_switch_free_fd(ethsw, &fd);
113462306a36Sopenharmony_ci		goto err_exit;
113562306a36Sopenharmony_ci	}
113662306a36Sopenharmony_ci
113762306a36Sopenharmony_ci	return NETDEV_TX_OK;
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_cierr_free_skb:
114062306a36Sopenharmony_ci	dev_kfree_skb(skb);
114162306a36Sopenharmony_cierr_exit:
114262306a36Sopenharmony_ci	return NETDEV_TX_OK;
114362306a36Sopenharmony_ci}
114462306a36Sopenharmony_ci
114562306a36Sopenharmony_cistatic int
114662306a36Sopenharmony_cidpaa2_switch_setup_tc_cls_flower(struct dpaa2_switch_filter_block *filter_block,
114762306a36Sopenharmony_ci				 struct flow_cls_offload *f)
114862306a36Sopenharmony_ci{
114962306a36Sopenharmony_ci	switch (f->command) {
115062306a36Sopenharmony_ci	case FLOW_CLS_REPLACE:
115162306a36Sopenharmony_ci		return dpaa2_switch_cls_flower_replace(filter_block, f);
115262306a36Sopenharmony_ci	case FLOW_CLS_DESTROY:
115362306a36Sopenharmony_ci		return dpaa2_switch_cls_flower_destroy(filter_block, f);
115462306a36Sopenharmony_ci	default:
115562306a36Sopenharmony_ci		return -EOPNOTSUPP;
115662306a36Sopenharmony_ci	}
115762306a36Sopenharmony_ci}
115862306a36Sopenharmony_ci
115962306a36Sopenharmony_cistatic int
116062306a36Sopenharmony_cidpaa2_switch_setup_tc_cls_matchall(struct dpaa2_switch_filter_block *block,
116162306a36Sopenharmony_ci				   struct tc_cls_matchall_offload *f)
116262306a36Sopenharmony_ci{
116362306a36Sopenharmony_ci	switch (f->command) {
116462306a36Sopenharmony_ci	case TC_CLSMATCHALL_REPLACE:
116562306a36Sopenharmony_ci		return dpaa2_switch_cls_matchall_replace(block, f);
116662306a36Sopenharmony_ci	case TC_CLSMATCHALL_DESTROY:
116762306a36Sopenharmony_ci		return dpaa2_switch_cls_matchall_destroy(block, f);
116862306a36Sopenharmony_ci	default:
116962306a36Sopenharmony_ci		return -EOPNOTSUPP;
117062306a36Sopenharmony_ci	}
117162306a36Sopenharmony_ci}
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_cistatic int dpaa2_switch_port_setup_tc_block_cb_ig(enum tc_setup_type type,
117462306a36Sopenharmony_ci						  void *type_data,
117562306a36Sopenharmony_ci						  void *cb_priv)
117662306a36Sopenharmony_ci{
117762306a36Sopenharmony_ci	switch (type) {
117862306a36Sopenharmony_ci	case TC_SETUP_CLSFLOWER:
117962306a36Sopenharmony_ci		return dpaa2_switch_setup_tc_cls_flower(cb_priv, type_data);
118062306a36Sopenharmony_ci	case TC_SETUP_CLSMATCHALL:
118162306a36Sopenharmony_ci		return dpaa2_switch_setup_tc_cls_matchall(cb_priv, type_data);
118262306a36Sopenharmony_ci	default:
118362306a36Sopenharmony_ci		return -EOPNOTSUPP;
118462306a36Sopenharmony_ci	}
118562306a36Sopenharmony_ci}
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_cistatic LIST_HEAD(dpaa2_switch_block_cb_list);
118862306a36Sopenharmony_ci
118962306a36Sopenharmony_cistatic int
119062306a36Sopenharmony_cidpaa2_switch_port_acl_tbl_bind(struct ethsw_port_priv *port_priv,
119162306a36Sopenharmony_ci			       struct dpaa2_switch_filter_block *block)
119262306a36Sopenharmony_ci{
119362306a36Sopenharmony_ci	struct ethsw_core *ethsw = port_priv->ethsw_data;
119462306a36Sopenharmony_ci	struct net_device *netdev = port_priv->netdev;
119562306a36Sopenharmony_ci	struct dpsw_acl_if_cfg acl_if_cfg;
119662306a36Sopenharmony_ci	int err;
119762306a36Sopenharmony_ci
119862306a36Sopenharmony_ci	if (port_priv->filter_block)
119962306a36Sopenharmony_ci		return -EINVAL;
120062306a36Sopenharmony_ci
120162306a36Sopenharmony_ci	acl_if_cfg.if_id[0] = port_priv->idx;
120262306a36Sopenharmony_ci	acl_if_cfg.num_ifs = 1;
120362306a36Sopenharmony_ci	err = dpsw_acl_add_if(ethsw->mc_io, 0, ethsw->dpsw_handle,
120462306a36Sopenharmony_ci			      block->acl_id, &acl_if_cfg);
120562306a36Sopenharmony_ci	if (err) {
120662306a36Sopenharmony_ci		netdev_err(netdev, "dpsw_acl_add_if err %d\n", err);
120762306a36Sopenharmony_ci		return err;
120862306a36Sopenharmony_ci	}
120962306a36Sopenharmony_ci
121062306a36Sopenharmony_ci	block->ports |= BIT(port_priv->idx);
121162306a36Sopenharmony_ci	port_priv->filter_block = block;
121262306a36Sopenharmony_ci
121362306a36Sopenharmony_ci	return 0;
121462306a36Sopenharmony_ci}
121562306a36Sopenharmony_ci
121662306a36Sopenharmony_cistatic int
121762306a36Sopenharmony_cidpaa2_switch_port_acl_tbl_unbind(struct ethsw_port_priv *port_priv,
121862306a36Sopenharmony_ci				 struct dpaa2_switch_filter_block *block)
121962306a36Sopenharmony_ci{
122062306a36Sopenharmony_ci	struct ethsw_core *ethsw = port_priv->ethsw_data;
122162306a36Sopenharmony_ci	struct net_device *netdev = port_priv->netdev;
122262306a36Sopenharmony_ci	struct dpsw_acl_if_cfg acl_if_cfg;
122362306a36Sopenharmony_ci	int err;
122462306a36Sopenharmony_ci
122562306a36Sopenharmony_ci	if (port_priv->filter_block != block)
122662306a36Sopenharmony_ci		return -EINVAL;
122762306a36Sopenharmony_ci
122862306a36Sopenharmony_ci	acl_if_cfg.if_id[0] = port_priv->idx;
122962306a36Sopenharmony_ci	acl_if_cfg.num_ifs = 1;
123062306a36Sopenharmony_ci	err = dpsw_acl_remove_if(ethsw->mc_io, 0, ethsw->dpsw_handle,
123162306a36Sopenharmony_ci				 block->acl_id, &acl_if_cfg);
123262306a36Sopenharmony_ci	if (err) {
123362306a36Sopenharmony_ci		netdev_err(netdev, "dpsw_acl_add_if err %d\n", err);
123462306a36Sopenharmony_ci		return err;
123562306a36Sopenharmony_ci	}
123662306a36Sopenharmony_ci
123762306a36Sopenharmony_ci	block->ports &= ~BIT(port_priv->idx);
123862306a36Sopenharmony_ci	port_priv->filter_block = NULL;
123962306a36Sopenharmony_ci	return 0;
124062306a36Sopenharmony_ci}
124162306a36Sopenharmony_ci
124262306a36Sopenharmony_cistatic int dpaa2_switch_port_block_bind(struct ethsw_port_priv *port_priv,
124362306a36Sopenharmony_ci					struct dpaa2_switch_filter_block *block)
124462306a36Sopenharmony_ci{
124562306a36Sopenharmony_ci	struct dpaa2_switch_filter_block *old_block = port_priv->filter_block;
124662306a36Sopenharmony_ci	int err;
124762306a36Sopenharmony_ci
124862306a36Sopenharmony_ci	/* Offload all the mirror entries found in the block on this new port
124962306a36Sopenharmony_ci	 * joining it.
125062306a36Sopenharmony_ci	 */
125162306a36Sopenharmony_ci	err = dpaa2_switch_block_offload_mirror(block, port_priv);
125262306a36Sopenharmony_ci	if (err)
125362306a36Sopenharmony_ci		return err;
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	/* If the port is already bound to this ACL table then do nothing. This
125662306a36Sopenharmony_ci	 * can happen when this port is the first one to join a tc block
125762306a36Sopenharmony_ci	 */
125862306a36Sopenharmony_ci	if (port_priv->filter_block == block)
125962306a36Sopenharmony_ci		return 0;
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	err = dpaa2_switch_port_acl_tbl_unbind(port_priv, old_block);
126262306a36Sopenharmony_ci	if (err)
126362306a36Sopenharmony_ci		return err;
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci	/* Mark the previous ACL table as being unused if this was the last
126662306a36Sopenharmony_ci	 * port that was using it.
126762306a36Sopenharmony_ci	 */
126862306a36Sopenharmony_ci	if (old_block->ports == 0)
126962306a36Sopenharmony_ci		old_block->in_use = false;
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	return dpaa2_switch_port_acl_tbl_bind(port_priv, block);
127262306a36Sopenharmony_ci}
127362306a36Sopenharmony_ci
127462306a36Sopenharmony_cistatic int
127562306a36Sopenharmony_cidpaa2_switch_port_block_unbind(struct ethsw_port_priv *port_priv,
127662306a36Sopenharmony_ci			       struct dpaa2_switch_filter_block *block)
127762306a36Sopenharmony_ci{
127862306a36Sopenharmony_ci	struct ethsw_core *ethsw = port_priv->ethsw_data;
127962306a36Sopenharmony_ci	struct dpaa2_switch_filter_block *new_block;
128062306a36Sopenharmony_ci	int err;
128162306a36Sopenharmony_ci
128262306a36Sopenharmony_ci	/* Unoffload all the mirror entries found in the block from the
128362306a36Sopenharmony_ci	 * port leaving it.
128462306a36Sopenharmony_ci	 */
128562306a36Sopenharmony_ci	err = dpaa2_switch_block_unoffload_mirror(block, port_priv);
128662306a36Sopenharmony_ci	if (err)
128762306a36Sopenharmony_ci		return err;
128862306a36Sopenharmony_ci
128962306a36Sopenharmony_ci	/* We are the last port that leaves a block (an ACL table).
129062306a36Sopenharmony_ci	 * We'll continue to use this table.
129162306a36Sopenharmony_ci	 */
129262306a36Sopenharmony_ci	if (block->ports == BIT(port_priv->idx))
129362306a36Sopenharmony_ci		return 0;
129462306a36Sopenharmony_ci
129562306a36Sopenharmony_ci	err = dpaa2_switch_port_acl_tbl_unbind(port_priv, block);
129662306a36Sopenharmony_ci	if (err)
129762306a36Sopenharmony_ci		return err;
129862306a36Sopenharmony_ci
129962306a36Sopenharmony_ci	if (block->ports == 0)
130062306a36Sopenharmony_ci		block->in_use = false;
130162306a36Sopenharmony_ci
130262306a36Sopenharmony_ci	new_block = dpaa2_switch_filter_block_get_unused(ethsw);
130362306a36Sopenharmony_ci	new_block->in_use = true;
130462306a36Sopenharmony_ci	return dpaa2_switch_port_acl_tbl_bind(port_priv, new_block);
130562306a36Sopenharmony_ci}
130662306a36Sopenharmony_ci
130762306a36Sopenharmony_cistatic int dpaa2_switch_setup_tc_block_bind(struct net_device *netdev,
130862306a36Sopenharmony_ci					    struct flow_block_offload *f)
130962306a36Sopenharmony_ci{
131062306a36Sopenharmony_ci	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
131162306a36Sopenharmony_ci	struct ethsw_core *ethsw = port_priv->ethsw_data;
131262306a36Sopenharmony_ci	struct dpaa2_switch_filter_block *filter_block;
131362306a36Sopenharmony_ci	struct flow_block_cb *block_cb;
131462306a36Sopenharmony_ci	bool register_block = false;
131562306a36Sopenharmony_ci	int err;
131662306a36Sopenharmony_ci
131762306a36Sopenharmony_ci	block_cb = flow_block_cb_lookup(f->block,
131862306a36Sopenharmony_ci					dpaa2_switch_port_setup_tc_block_cb_ig,
131962306a36Sopenharmony_ci					ethsw);
132062306a36Sopenharmony_ci
132162306a36Sopenharmony_ci	if (!block_cb) {
132262306a36Sopenharmony_ci		/* If the filter block is not already known, then this port
132362306a36Sopenharmony_ci		 * must be the first to join it. In this case, we can just
132462306a36Sopenharmony_ci		 * continue to use our private table
132562306a36Sopenharmony_ci		 */
132662306a36Sopenharmony_ci		filter_block = port_priv->filter_block;
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci		block_cb = flow_block_cb_alloc(dpaa2_switch_port_setup_tc_block_cb_ig,
132962306a36Sopenharmony_ci					       ethsw, filter_block, NULL);
133062306a36Sopenharmony_ci		if (IS_ERR(block_cb))
133162306a36Sopenharmony_ci			return PTR_ERR(block_cb);
133262306a36Sopenharmony_ci
133362306a36Sopenharmony_ci		register_block = true;
133462306a36Sopenharmony_ci	} else {
133562306a36Sopenharmony_ci		filter_block = flow_block_cb_priv(block_cb);
133662306a36Sopenharmony_ci	}
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	flow_block_cb_incref(block_cb);
133962306a36Sopenharmony_ci	err = dpaa2_switch_port_block_bind(port_priv, filter_block);
134062306a36Sopenharmony_ci	if (err)
134162306a36Sopenharmony_ci		goto err_block_bind;
134262306a36Sopenharmony_ci
134362306a36Sopenharmony_ci	if (register_block) {
134462306a36Sopenharmony_ci		flow_block_cb_add(block_cb, f);
134562306a36Sopenharmony_ci		list_add_tail(&block_cb->driver_list,
134662306a36Sopenharmony_ci			      &dpaa2_switch_block_cb_list);
134762306a36Sopenharmony_ci	}
134862306a36Sopenharmony_ci
134962306a36Sopenharmony_ci	return 0;
135062306a36Sopenharmony_ci
135162306a36Sopenharmony_cierr_block_bind:
135262306a36Sopenharmony_ci	if (!flow_block_cb_decref(block_cb))
135362306a36Sopenharmony_ci		flow_block_cb_free(block_cb);
135462306a36Sopenharmony_ci	return err;
135562306a36Sopenharmony_ci}
135662306a36Sopenharmony_ci
135762306a36Sopenharmony_cistatic void dpaa2_switch_setup_tc_block_unbind(struct net_device *netdev,
135862306a36Sopenharmony_ci					       struct flow_block_offload *f)
135962306a36Sopenharmony_ci{
136062306a36Sopenharmony_ci	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
136162306a36Sopenharmony_ci	struct ethsw_core *ethsw = port_priv->ethsw_data;
136262306a36Sopenharmony_ci	struct dpaa2_switch_filter_block *filter_block;
136362306a36Sopenharmony_ci	struct flow_block_cb *block_cb;
136462306a36Sopenharmony_ci	int err;
136562306a36Sopenharmony_ci
136662306a36Sopenharmony_ci	block_cb = flow_block_cb_lookup(f->block,
136762306a36Sopenharmony_ci					dpaa2_switch_port_setup_tc_block_cb_ig,
136862306a36Sopenharmony_ci					ethsw);
136962306a36Sopenharmony_ci	if (!block_cb)
137062306a36Sopenharmony_ci		return;
137162306a36Sopenharmony_ci
137262306a36Sopenharmony_ci	filter_block = flow_block_cb_priv(block_cb);
137362306a36Sopenharmony_ci	err = dpaa2_switch_port_block_unbind(port_priv, filter_block);
137462306a36Sopenharmony_ci	if (!err && !flow_block_cb_decref(block_cb)) {
137562306a36Sopenharmony_ci		flow_block_cb_remove(block_cb, f);
137662306a36Sopenharmony_ci		list_del(&block_cb->driver_list);
137762306a36Sopenharmony_ci	}
137862306a36Sopenharmony_ci}
137962306a36Sopenharmony_ci
138062306a36Sopenharmony_cistatic int dpaa2_switch_setup_tc_block(struct net_device *netdev,
138162306a36Sopenharmony_ci				       struct flow_block_offload *f)
138262306a36Sopenharmony_ci{
138362306a36Sopenharmony_ci	if (f->binder_type != FLOW_BLOCK_BINDER_TYPE_CLSACT_INGRESS)
138462306a36Sopenharmony_ci		return -EOPNOTSUPP;
138562306a36Sopenharmony_ci
138662306a36Sopenharmony_ci	f->driver_block_list = &dpaa2_switch_block_cb_list;
138762306a36Sopenharmony_ci
138862306a36Sopenharmony_ci	switch (f->command) {
138962306a36Sopenharmony_ci	case FLOW_BLOCK_BIND:
139062306a36Sopenharmony_ci		return dpaa2_switch_setup_tc_block_bind(netdev, f);
139162306a36Sopenharmony_ci	case FLOW_BLOCK_UNBIND:
139262306a36Sopenharmony_ci		dpaa2_switch_setup_tc_block_unbind(netdev, f);
139362306a36Sopenharmony_ci		return 0;
139462306a36Sopenharmony_ci	default:
139562306a36Sopenharmony_ci		return -EOPNOTSUPP;
139662306a36Sopenharmony_ci	}
139762306a36Sopenharmony_ci}
139862306a36Sopenharmony_ci
139962306a36Sopenharmony_cistatic int dpaa2_switch_port_setup_tc(struct net_device *netdev,
140062306a36Sopenharmony_ci				      enum tc_setup_type type,
140162306a36Sopenharmony_ci				      void *type_data)
140262306a36Sopenharmony_ci{
140362306a36Sopenharmony_ci	switch (type) {
140462306a36Sopenharmony_ci	case TC_SETUP_BLOCK: {
140562306a36Sopenharmony_ci		return dpaa2_switch_setup_tc_block(netdev, type_data);
140662306a36Sopenharmony_ci	}
140762306a36Sopenharmony_ci	default:
140862306a36Sopenharmony_ci		return -EOPNOTSUPP;
140962306a36Sopenharmony_ci	}
141062306a36Sopenharmony_ci
141162306a36Sopenharmony_ci	return 0;
141262306a36Sopenharmony_ci}
141362306a36Sopenharmony_ci
141462306a36Sopenharmony_cistatic const struct net_device_ops dpaa2_switch_port_ops = {
141562306a36Sopenharmony_ci	.ndo_open		= dpaa2_switch_port_open,
141662306a36Sopenharmony_ci	.ndo_stop		= dpaa2_switch_port_stop,
141762306a36Sopenharmony_ci
141862306a36Sopenharmony_ci	.ndo_set_mac_address	= eth_mac_addr,
141962306a36Sopenharmony_ci	.ndo_get_stats64	= dpaa2_switch_port_get_stats,
142062306a36Sopenharmony_ci	.ndo_change_mtu		= dpaa2_switch_port_change_mtu,
142162306a36Sopenharmony_ci	.ndo_has_offload_stats	= dpaa2_switch_port_has_offload_stats,
142262306a36Sopenharmony_ci	.ndo_get_offload_stats	= dpaa2_switch_port_get_offload_stats,
142362306a36Sopenharmony_ci	.ndo_fdb_dump		= dpaa2_switch_port_fdb_dump,
142462306a36Sopenharmony_ci	.ndo_vlan_rx_add_vid	= dpaa2_switch_port_vlan_add,
142562306a36Sopenharmony_ci	.ndo_vlan_rx_kill_vid	= dpaa2_switch_port_vlan_kill,
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_ci	.ndo_start_xmit		= dpaa2_switch_port_tx,
142862306a36Sopenharmony_ci	.ndo_get_port_parent_id	= dpaa2_switch_port_parent_id,
142962306a36Sopenharmony_ci	.ndo_get_phys_port_name = dpaa2_switch_port_get_phys_name,
143062306a36Sopenharmony_ci	.ndo_setup_tc		= dpaa2_switch_port_setup_tc,
143162306a36Sopenharmony_ci};
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_cibool dpaa2_switch_port_dev_check(const struct net_device *netdev)
143462306a36Sopenharmony_ci{
143562306a36Sopenharmony_ci	return netdev->netdev_ops == &dpaa2_switch_port_ops;
143662306a36Sopenharmony_ci}
143762306a36Sopenharmony_ci
143862306a36Sopenharmony_cistatic int dpaa2_switch_port_connect_mac(struct ethsw_port_priv *port_priv)
143962306a36Sopenharmony_ci{
144062306a36Sopenharmony_ci	struct fsl_mc_device *dpsw_port_dev, *dpmac_dev;
144162306a36Sopenharmony_ci	struct dpaa2_mac *mac;
144262306a36Sopenharmony_ci	int err;
144362306a36Sopenharmony_ci
144462306a36Sopenharmony_ci	dpsw_port_dev = to_fsl_mc_device(port_priv->netdev->dev.parent);
144562306a36Sopenharmony_ci	dpmac_dev = fsl_mc_get_endpoint(dpsw_port_dev, port_priv->idx);
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	if (PTR_ERR(dpmac_dev) == -EPROBE_DEFER)
144862306a36Sopenharmony_ci		return PTR_ERR(dpmac_dev);
144962306a36Sopenharmony_ci
145062306a36Sopenharmony_ci	if (IS_ERR(dpmac_dev) || dpmac_dev->dev.type != &fsl_mc_bus_dpmac_type)
145162306a36Sopenharmony_ci		return 0;
145262306a36Sopenharmony_ci
145362306a36Sopenharmony_ci	mac = kzalloc(sizeof(*mac), GFP_KERNEL);
145462306a36Sopenharmony_ci	if (!mac)
145562306a36Sopenharmony_ci		return -ENOMEM;
145662306a36Sopenharmony_ci
145762306a36Sopenharmony_ci	mac->mc_dev = dpmac_dev;
145862306a36Sopenharmony_ci	mac->mc_io = port_priv->ethsw_data->mc_io;
145962306a36Sopenharmony_ci	mac->net_dev = port_priv->netdev;
146062306a36Sopenharmony_ci
146162306a36Sopenharmony_ci	err = dpaa2_mac_open(mac);
146262306a36Sopenharmony_ci	if (err)
146362306a36Sopenharmony_ci		goto err_free_mac;
146462306a36Sopenharmony_ci
146562306a36Sopenharmony_ci	if (dpaa2_mac_is_type_phy(mac)) {
146662306a36Sopenharmony_ci		err = dpaa2_mac_connect(mac);
146762306a36Sopenharmony_ci		if (err) {
146862306a36Sopenharmony_ci			netdev_err(port_priv->netdev,
146962306a36Sopenharmony_ci				   "Error connecting to the MAC endpoint %pe\n",
147062306a36Sopenharmony_ci				   ERR_PTR(err));
147162306a36Sopenharmony_ci			goto err_close_mac;
147262306a36Sopenharmony_ci		}
147362306a36Sopenharmony_ci	}
147462306a36Sopenharmony_ci
147562306a36Sopenharmony_ci	mutex_lock(&port_priv->mac_lock);
147662306a36Sopenharmony_ci	port_priv->mac = mac;
147762306a36Sopenharmony_ci	mutex_unlock(&port_priv->mac_lock);
147862306a36Sopenharmony_ci
147962306a36Sopenharmony_ci	return 0;
148062306a36Sopenharmony_ci
148162306a36Sopenharmony_cierr_close_mac:
148262306a36Sopenharmony_ci	dpaa2_mac_close(mac);
148362306a36Sopenharmony_cierr_free_mac:
148462306a36Sopenharmony_ci	kfree(mac);
148562306a36Sopenharmony_ci	return err;
148662306a36Sopenharmony_ci}
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_cistatic void dpaa2_switch_port_disconnect_mac(struct ethsw_port_priv *port_priv)
148962306a36Sopenharmony_ci{
149062306a36Sopenharmony_ci	struct dpaa2_mac *mac;
149162306a36Sopenharmony_ci
149262306a36Sopenharmony_ci	mutex_lock(&port_priv->mac_lock);
149362306a36Sopenharmony_ci	mac = port_priv->mac;
149462306a36Sopenharmony_ci	port_priv->mac = NULL;
149562306a36Sopenharmony_ci	mutex_unlock(&port_priv->mac_lock);
149662306a36Sopenharmony_ci
149762306a36Sopenharmony_ci	if (!mac)
149862306a36Sopenharmony_ci		return;
149962306a36Sopenharmony_ci
150062306a36Sopenharmony_ci	if (dpaa2_mac_is_type_phy(mac))
150162306a36Sopenharmony_ci		dpaa2_mac_disconnect(mac);
150262306a36Sopenharmony_ci
150362306a36Sopenharmony_ci	dpaa2_mac_close(mac);
150462306a36Sopenharmony_ci	kfree(mac);
150562306a36Sopenharmony_ci}
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_cistatic irqreturn_t dpaa2_switch_irq0_handler_thread(int irq_num, void *arg)
150862306a36Sopenharmony_ci{
150962306a36Sopenharmony_ci	struct device *dev = (struct device *)arg;
151062306a36Sopenharmony_ci	struct ethsw_core *ethsw = dev_get_drvdata(dev);
151162306a36Sopenharmony_ci	struct ethsw_port_priv *port_priv;
151262306a36Sopenharmony_ci	u32 status = ~0;
151362306a36Sopenharmony_ci	int err, if_id;
151462306a36Sopenharmony_ci	bool had_mac;
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci	err = dpsw_get_irq_status(ethsw->mc_io, 0, ethsw->dpsw_handle,
151762306a36Sopenharmony_ci				  DPSW_IRQ_INDEX_IF, &status);
151862306a36Sopenharmony_ci	if (err) {
151962306a36Sopenharmony_ci		dev_err(dev, "Can't get irq status (err %d)\n", err);
152062306a36Sopenharmony_ci		goto out;
152162306a36Sopenharmony_ci	}
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	if_id = (status & 0xFFFF0000) >> 16;
152462306a36Sopenharmony_ci	port_priv = ethsw->ports[if_id];
152562306a36Sopenharmony_ci
152662306a36Sopenharmony_ci	if (status & DPSW_IRQ_EVENT_LINK_CHANGED) {
152762306a36Sopenharmony_ci		dpaa2_switch_port_link_state_update(port_priv->netdev);
152862306a36Sopenharmony_ci		dpaa2_switch_port_set_mac_addr(port_priv);
152962306a36Sopenharmony_ci	}
153062306a36Sopenharmony_ci
153162306a36Sopenharmony_ci	if (status & DPSW_IRQ_EVENT_ENDPOINT_CHANGED) {
153262306a36Sopenharmony_ci		/* We can avoid locking because the "endpoint changed" IRQ
153362306a36Sopenharmony_ci		 * handler is the only one who changes priv->mac at runtime,
153462306a36Sopenharmony_ci		 * so we are not racing with anyone.
153562306a36Sopenharmony_ci		 */
153662306a36Sopenharmony_ci		had_mac = !!port_priv->mac;
153762306a36Sopenharmony_ci		if (had_mac)
153862306a36Sopenharmony_ci			dpaa2_switch_port_disconnect_mac(port_priv);
153962306a36Sopenharmony_ci		else
154062306a36Sopenharmony_ci			dpaa2_switch_port_connect_mac(port_priv);
154162306a36Sopenharmony_ci	}
154262306a36Sopenharmony_ci
154362306a36Sopenharmony_ciout:
154462306a36Sopenharmony_ci	err = dpsw_clear_irq_status(ethsw->mc_io, 0, ethsw->dpsw_handle,
154562306a36Sopenharmony_ci				    DPSW_IRQ_INDEX_IF, status);
154662306a36Sopenharmony_ci	if (err)
154762306a36Sopenharmony_ci		dev_err(dev, "Can't clear irq status (err %d)\n", err);
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_ci	return IRQ_HANDLED;
155062306a36Sopenharmony_ci}
155162306a36Sopenharmony_ci
155262306a36Sopenharmony_cistatic int dpaa2_switch_setup_irqs(struct fsl_mc_device *sw_dev)
155362306a36Sopenharmony_ci{
155462306a36Sopenharmony_ci	struct device *dev = &sw_dev->dev;
155562306a36Sopenharmony_ci	struct ethsw_core *ethsw = dev_get_drvdata(dev);
155662306a36Sopenharmony_ci	u32 mask = DPSW_IRQ_EVENT_LINK_CHANGED;
155762306a36Sopenharmony_ci	struct fsl_mc_device_irq *irq;
155862306a36Sopenharmony_ci	int err;
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci	err = fsl_mc_allocate_irqs(sw_dev);
156162306a36Sopenharmony_ci	if (err) {
156262306a36Sopenharmony_ci		dev_err(dev, "MC irqs allocation failed\n");
156362306a36Sopenharmony_ci		return err;
156462306a36Sopenharmony_ci	}
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	if (WARN_ON(sw_dev->obj_desc.irq_count != DPSW_IRQ_NUM)) {
156762306a36Sopenharmony_ci		err = -EINVAL;
156862306a36Sopenharmony_ci		goto free_irq;
156962306a36Sopenharmony_ci	}
157062306a36Sopenharmony_ci
157162306a36Sopenharmony_ci	err = dpsw_set_irq_enable(ethsw->mc_io, 0, ethsw->dpsw_handle,
157262306a36Sopenharmony_ci				  DPSW_IRQ_INDEX_IF, 0);
157362306a36Sopenharmony_ci	if (err) {
157462306a36Sopenharmony_ci		dev_err(dev, "dpsw_set_irq_enable err %d\n", err);
157562306a36Sopenharmony_ci		goto free_irq;
157662306a36Sopenharmony_ci	}
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_ci	irq = sw_dev->irqs[DPSW_IRQ_INDEX_IF];
157962306a36Sopenharmony_ci
158062306a36Sopenharmony_ci	err = devm_request_threaded_irq(dev, irq->virq, NULL,
158162306a36Sopenharmony_ci					dpaa2_switch_irq0_handler_thread,
158262306a36Sopenharmony_ci					IRQF_NO_SUSPEND | IRQF_ONESHOT,
158362306a36Sopenharmony_ci					dev_name(dev), dev);
158462306a36Sopenharmony_ci	if (err) {
158562306a36Sopenharmony_ci		dev_err(dev, "devm_request_threaded_irq(): %d\n", err);
158662306a36Sopenharmony_ci		goto free_irq;
158762306a36Sopenharmony_ci	}
158862306a36Sopenharmony_ci
158962306a36Sopenharmony_ci	err = dpsw_set_irq_mask(ethsw->mc_io, 0, ethsw->dpsw_handle,
159062306a36Sopenharmony_ci				DPSW_IRQ_INDEX_IF, mask);
159162306a36Sopenharmony_ci	if (err) {
159262306a36Sopenharmony_ci		dev_err(dev, "dpsw_set_irq_mask(): %d\n", err);
159362306a36Sopenharmony_ci		goto free_devm_irq;
159462306a36Sopenharmony_ci	}
159562306a36Sopenharmony_ci
159662306a36Sopenharmony_ci	err = dpsw_set_irq_enable(ethsw->mc_io, 0, ethsw->dpsw_handle,
159762306a36Sopenharmony_ci				  DPSW_IRQ_INDEX_IF, 1);
159862306a36Sopenharmony_ci	if (err) {
159962306a36Sopenharmony_ci		dev_err(dev, "dpsw_set_irq_enable(): %d\n", err);
160062306a36Sopenharmony_ci		goto free_devm_irq;
160162306a36Sopenharmony_ci	}
160262306a36Sopenharmony_ci
160362306a36Sopenharmony_ci	return 0;
160462306a36Sopenharmony_ci
160562306a36Sopenharmony_cifree_devm_irq:
160662306a36Sopenharmony_ci	devm_free_irq(dev, irq->virq, dev);
160762306a36Sopenharmony_cifree_irq:
160862306a36Sopenharmony_ci	fsl_mc_free_irqs(sw_dev);
160962306a36Sopenharmony_ci	return err;
161062306a36Sopenharmony_ci}
161162306a36Sopenharmony_ci
161262306a36Sopenharmony_cistatic void dpaa2_switch_teardown_irqs(struct fsl_mc_device *sw_dev)
161362306a36Sopenharmony_ci{
161462306a36Sopenharmony_ci	struct device *dev = &sw_dev->dev;
161562306a36Sopenharmony_ci	struct ethsw_core *ethsw = dev_get_drvdata(dev);
161662306a36Sopenharmony_ci	int err;
161762306a36Sopenharmony_ci
161862306a36Sopenharmony_ci	err = dpsw_set_irq_enable(ethsw->mc_io, 0, ethsw->dpsw_handle,
161962306a36Sopenharmony_ci				  DPSW_IRQ_INDEX_IF, 0);
162062306a36Sopenharmony_ci	if (err)
162162306a36Sopenharmony_ci		dev_err(dev, "dpsw_set_irq_enable err %d\n", err);
162262306a36Sopenharmony_ci
162362306a36Sopenharmony_ci	fsl_mc_free_irqs(sw_dev);
162462306a36Sopenharmony_ci}
162562306a36Sopenharmony_ci
162662306a36Sopenharmony_cistatic int dpaa2_switch_port_set_learning(struct ethsw_port_priv *port_priv, bool enable)
162762306a36Sopenharmony_ci{
162862306a36Sopenharmony_ci	struct ethsw_core *ethsw = port_priv->ethsw_data;
162962306a36Sopenharmony_ci	enum dpsw_learning_mode learn_mode;
163062306a36Sopenharmony_ci	int err;
163162306a36Sopenharmony_ci
163262306a36Sopenharmony_ci	if (enable)
163362306a36Sopenharmony_ci		learn_mode = DPSW_LEARNING_MODE_HW;
163462306a36Sopenharmony_ci	else
163562306a36Sopenharmony_ci		learn_mode = DPSW_LEARNING_MODE_DIS;
163662306a36Sopenharmony_ci
163762306a36Sopenharmony_ci	err = dpsw_if_set_learning_mode(ethsw->mc_io, 0, ethsw->dpsw_handle,
163862306a36Sopenharmony_ci					port_priv->idx, learn_mode);
163962306a36Sopenharmony_ci	if (err)
164062306a36Sopenharmony_ci		netdev_err(port_priv->netdev, "dpsw_if_set_learning_mode err %d\n", err);
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci	if (!enable)
164362306a36Sopenharmony_ci		dpaa2_switch_port_fast_age(port_priv);
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci	return err;
164662306a36Sopenharmony_ci}
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_cistatic int dpaa2_switch_port_attr_stp_state_set(struct net_device *netdev,
164962306a36Sopenharmony_ci						u8 state)
165062306a36Sopenharmony_ci{
165162306a36Sopenharmony_ci	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
165262306a36Sopenharmony_ci	int err;
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci	err = dpaa2_switch_port_set_stp_state(port_priv, state);
165562306a36Sopenharmony_ci	if (err)
165662306a36Sopenharmony_ci		return err;
165762306a36Sopenharmony_ci
165862306a36Sopenharmony_ci	switch (state) {
165962306a36Sopenharmony_ci	case BR_STATE_DISABLED:
166062306a36Sopenharmony_ci	case BR_STATE_BLOCKING:
166162306a36Sopenharmony_ci	case BR_STATE_LISTENING:
166262306a36Sopenharmony_ci		err = dpaa2_switch_port_set_learning(port_priv, false);
166362306a36Sopenharmony_ci		break;
166462306a36Sopenharmony_ci	case BR_STATE_LEARNING:
166562306a36Sopenharmony_ci	case BR_STATE_FORWARDING:
166662306a36Sopenharmony_ci		err = dpaa2_switch_port_set_learning(port_priv,
166762306a36Sopenharmony_ci						     port_priv->learn_ena);
166862306a36Sopenharmony_ci		break;
166962306a36Sopenharmony_ci	}
167062306a36Sopenharmony_ci
167162306a36Sopenharmony_ci	return err;
167262306a36Sopenharmony_ci}
167362306a36Sopenharmony_ci
167462306a36Sopenharmony_cistatic int dpaa2_switch_port_flood(struct ethsw_port_priv *port_priv,
167562306a36Sopenharmony_ci				   struct switchdev_brport_flags flags)
167662306a36Sopenharmony_ci{
167762306a36Sopenharmony_ci	struct ethsw_core *ethsw = port_priv->ethsw_data;
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_ci	if (flags.mask & BR_BCAST_FLOOD)
168062306a36Sopenharmony_ci		port_priv->bcast_flood = !!(flags.val & BR_BCAST_FLOOD);
168162306a36Sopenharmony_ci
168262306a36Sopenharmony_ci	if (flags.mask & BR_FLOOD)
168362306a36Sopenharmony_ci		port_priv->ucast_flood = !!(flags.val & BR_FLOOD);
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci	return dpaa2_switch_fdb_set_egress_flood(ethsw, port_priv->fdb->fdb_id);
168662306a36Sopenharmony_ci}
168762306a36Sopenharmony_ci
168862306a36Sopenharmony_cistatic int dpaa2_switch_port_pre_bridge_flags(struct net_device *netdev,
168962306a36Sopenharmony_ci					      struct switchdev_brport_flags flags,
169062306a36Sopenharmony_ci					      struct netlink_ext_ack *extack)
169162306a36Sopenharmony_ci{
169262306a36Sopenharmony_ci	if (flags.mask & ~(BR_LEARNING | BR_BCAST_FLOOD | BR_FLOOD |
169362306a36Sopenharmony_ci			   BR_MCAST_FLOOD))
169462306a36Sopenharmony_ci		return -EINVAL;
169562306a36Sopenharmony_ci
169662306a36Sopenharmony_ci	if (flags.mask & (BR_FLOOD | BR_MCAST_FLOOD)) {
169762306a36Sopenharmony_ci		bool multicast = !!(flags.val & BR_MCAST_FLOOD);
169862306a36Sopenharmony_ci		bool unicast = !!(flags.val & BR_FLOOD);
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_ci		if (unicast != multicast) {
170162306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack,
170262306a36Sopenharmony_ci					   "Cannot configure multicast flooding independently of unicast");
170362306a36Sopenharmony_ci			return -EINVAL;
170462306a36Sopenharmony_ci		}
170562306a36Sopenharmony_ci	}
170662306a36Sopenharmony_ci
170762306a36Sopenharmony_ci	return 0;
170862306a36Sopenharmony_ci}
170962306a36Sopenharmony_ci
171062306a36Sopenharmony_cistatic int dpaa2_switch_port_bridge_flags(struct net_device *netdev,
171162306a36Sopenharmony_ci					  struct switchdev_brport_flags flags,
171262306a36Sopenharmony_ci					  struct netlink_ext_ack *extack)
171362306a36Sopenharmony_ci{
171462306a36Sopenharmony_ci	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
171562306a36Sopenharmony_ci	int err;
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_ci	if (flags.mask & BR_LEARNING) {
171862306a36Sopenharmony_ci		bool learn_ena = !!(flags.val & BR_LEARNING);
171962306a36Sopenharmony_ci
172062306a36Sopenharmony_ci		err = dpaa2_switch_port_set_learning(port_priv, learn_ena);
172162306a36Sopenharmony_ci		if (err)
172262306a36Sopenharmony_ci			return err;
172362306a36Sopenharmony_ci		port_priv->learn_ena = learn_ena;
172462306a36Sopenharmony_ci	}
172562306a36Sopenharmony_ci
172662306a36Sopenharmony_ci	if (flags.mask & (BR_BCAST_FLOOD | BR_FLOOD | BR_MCAST_FLOOD)) {
172762306a36Sopenharmony_ci		err = dpaa2_switch_port_flood(port_priv, flags);
172862306a36Sopenharmony_ci		if (err)
172962306a36Sopenharmony_ci			return err;
173062306a36Sopenharmony_ci	}
173162306a36Sopenharmony_ci
173262306a36Sopenharmony_ci	return 0;
173362306a36Sopenharmony_ci}
173462306a36Sopenharmony_ci
173562306a36Sopenharmony_cistatic int dpaa2_switch_port_attr_set(struct net_device *netdev, const void *ctx,
173662306a36Sopenharmony_ci				      const struct switchdev_attr *attr,
173762306a36Sopenharmony_ci				      struct netlink_ext_ack *extack)
173862306a36Sopenharmony_ci{
173962306a36Sopenharmony_ci	int err = 0;
174062306a36Sopenharmony_ci
174162306a36Sopenharmony_ci	switch (attr->id) {
174262306a36Sopenharmony_ci	case SWITCHDEV_ATTR_ID_PORT_STP_STATE:
174362306a36Sopenharmony_ci		err = dpaa2_switch_port_attr_stp_state_set(netdev,
174462306a36Sopenharmony_ci							   attr->u.stp_state);
174562306a36Sopenharmony_ci		break;
174662306a36Sopenharmony_ci	case SWITCHDEV_ATTR_ID_BRIDGE_VLAN_FILTERING:
174762306a36Sopenharmony_ci		if (!attr->u.vlan_filtering) {
174862306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack,
174962306a36Sopenharmony_ci					   "The DPAA2 switch does not support VLAN-unaware operation");
175062306a36Sopenharmony_ci			return -EOPNOTSUPP;
175162306a36Sopenharmony_ci		}
175262306a36Sopenharmony_ci		break;
175362306a36Sopenharmony_ci	case SWITCHDEV_ATTR_ID_PORT_PRE_BRIDGE_FLAGS:
175462306a36Sopenharmony_ci		err = dpaa2_switch_port_pre_bridge_flags(netdev, attr->u.brport_flags, extack);
175562306a36Sopenharmony_ci		break;
175662306a36Sopenharmony_ci	case SWITCHDEV_ATTR_ID_PORT_BRIDGE_FLAGS:
175762306a36Sopenharmony_ci		err = dpaa2_switch_port_bridge_flags(netdev, attr->u.brport_flags, extack);
175862306a36Sopenharmony_ci		break;
175962306a36Sopenharmony_ci	default:
176062306a36Sopenharmony_ci		err = -EOPNOTSUPP;
176162306a36Sopenharmony_ci		break;
176262306a36Sopenharmony_ci	}
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_ci	return err;
176562306a36Sopenharmony_ci}
176662306a36Sopenharmony_ci
176762306a36Sopenharmony_ciint dpaa2_switch_port_vlans_add(struct net_device *netdev,
176862306a36Sopenharmony_ci				const struct switchdev_obj_port_vlan *vlan)
176962306a36Sopenharmony_ci{
177062306a36Sopenharmony_ci	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
177162306a36Sopenharmony_ci	struct ethsw_core *ethsw = port_priv->ethsw_data;
177262306a36Sopenharmony_ci	struct dpsw_attr *attr = &ethsw->sw_attr;
177362306a36Sopenharmony_ci	int err = 0;
177462306a36Sopenharmony_ci
177562306a36Sopenharmony_ci	/* Make sure that the VLAN is not already configured
177662306a36Sopenharmony_ci	 * on the switch port
177762306a36Sopenharmony_ci	 */
177862306a36Sopenharmony_ci	if (port_priv->vlans[vlan->vid] & ETHSW_VLAN_MEMBER)
177962306a36Sopenharmony_ci		return -EEXIST;
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_ci	/* Check if there is space for a new VLAN */
178262306a36Sopenharmony_ci	err = dpsw_get_attributes(ethsw->mc_io, 0, ethsw->dpsw_handle,
178362306a36Sopenharmony_ci				  &ethsw->sw_attr);
178462306a36Sopenharmony_ci	if (err) {
178562306a36Sopenharmony_ci		netdev_err(netdev, "dpsw_get_attributes err %d\n", err);
178662306a36Sopenharmony_ci		return err;
178762306a36Sopenharmony_ci	}
178862306a36Sopenharmony_ci	if (attr->max_vlans - attr->num_vlans < 1)
178962306a36Sopenharmony_ci		return -ENOSPC;
179062306a36Sopenharmony_ci
179162306a36Sopenharmony_ci	/* Check if there is space for a new VLAN */
179262306a36Sopenharmony_ci	err = dpsw_get_attributes(ethsw->mc_io, 0, ethsw->dpsw_handle,
179362306a36Sopenharmony_ci				  &ethsw->sw_attr);
179462306a36Sopenharmony_ci	if (err) {
179562306a36Sopenharmony_ci		netdev_err(netdev, "dpsw_get_attributes err %d\n", err);
179662306a36Sopenharmony_ci		return err;
179762306a36Sopenharmony_ci	}
179862306a36Sopenharmony_ci	if (attr->max_vlans - attr->num_vlans < 1)
179962306a36Sopenharmony_ci		return -ENOSPC;
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_ci	if (!port_priv->ethsw_data->vlans[vlan->vid]) {
180262306a36Sopenharmony_ci		/* this is a new VLAN */
180362306a36Sopenharmony_ci		err = dpaa2_switch_add_vlan(port_priv, vlan->vid);
180462306a36Sopenharmony_ci		if (err)
180562306a36Sopenharmony_ci			return err;
180662306a36Sopenharmony_ci
180762306a36Sopenharmony_ci		port_priv->ethsw_data->vlans[vlan->vid] |= ETHSW_VLAN_GLOBAL;
180862306a36Sopenharmony_ci	}
180962306a36Sopenharmony_ci
181062306a36Sopenharmony_ci	return dpaa2_switch_port_add_vlan(port_priv, vlan->vid, vlan->flags);
181162306a36Sopenharmony_ci}
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_cistatic int dpaa2_switch_port_lookup_address(struct net_device *netdev, int is_uc,
181462306a36Sopenharmony_ci					    const unsigned char *addr)
181562306a36Sopenharmony_ci{
181662306a36Sopenharmony_ci	struct netdev_hw_addr_list *list = (is_uc) ? &netdev->uc : &netdev->mc;
181762306a36Sopenharmony_ci	struct netdev_hw_addr *ha;
181862306a36Sopenharmony_ci
181962306a36Sopenharmony_ci	netif_addr_lock_bh(netdev);
182062306a36Sopenharmony_ci	list_for_each_entry(ha, &list->list, list) {
182162306a36Sopenharmony_ci		if (ether_addr_equal(ha->addr, addr)) {
182262306a36Sopenharmony_ci			netif_addr_unlock_bh(netdev);
182362306a36Sopenharmony_ci			return 1;
182462306a36Sopenharmony_ci		}
182562306a36Sopenharmony_ci	}
182662306a36Sopenharmony_ci	netif_addr_unlock_bh(netdev);
182762306a36Sopenharmony_ci	return 0;
182862306a36Sopenharmony_ci}
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_cistatic int dpaa2_switch_port_mdb_add(struct net_device *netdev,
183162306a36Sopenharmony_ci				     const struct switchdev_obj_port_mdb *mdb)
183262306a36Sopenharmony_ci{
183362306a36Sopenharmony_ci	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
183462306a36Sopenharmony_ci	int err;
183562306a36Sopenharmony_ci
183662306a36Sopenharmony_ci	/* Check if address is already set on this port */
183762306a36Sopenharmony_ci	if (dpaa2_switch_port_lookup_address(netdev, 0, mdb->addr))
183862306a36Sopenharmony_ci		return -EEXIST;
183962306a36Sopenharmony_ci
184062306a36Sopenharmony_ci	err = dpaa2_switch_port_fdb_add_mc(port_priv, mdb->addr);
184162306a36Sopenharmony_ci	if (err)
184262306a36Sopenharmony_ci		return err;
184362306a36Sopenharmony_ci
184462306a36Sopenharmony_ci	err = dev_mc_add(netdev, mdb->addr);
184562306a36Sopenharmony_ci	if (err) {
184662306a36Sopenharmony_ci		netdev_err(netdev, "dev_mc_add err %d\n", err);
184762306a36Sopenharmony_ci		dpaa2_switch_port_fdb_del_mc(port_priv, mdb->addr);
184862306a36Sopenharmony_ci	}
184962306a36Sopenharmony_ci
185062306a36Sopenharmony_ci	return err;
185162306a36Sopenharmony_ci}
185262306a36Sopenharmony_ci
185362306a36Sopenharmony_cistatic int dpaa2_switch_port_obj_add(struct net_device *netdev,
185462306a36Sopenharmony_ci				     const struct switchdev_obj *obj)
185562306a36Sopenharmony_ci{
185662306a36Sopenharmony_ci	int err;
185762306a36Sopenharmony_ci
185862306a36Sopenharmony_ci	switch (obj->id) {
185962306a36Sopenharmony_ci	case SWITCHDEV_OBJ_ID_PORT_VLAN:
186062306a36Sopenharmony_ci		err = dpaa2_switch_port_vlans_add(netdev,
186162306a36Sopenharmony_ci						  SWITCHDEV_OBJ_PORT_VLAN(obj));
186262306a36Sopenharmony_ci		break;
186362306a36Sopenharmony_ci	case SWITCHDEV_OBJ_ID_PORT_MDB:
186462306a36Sopenharmony_ci		err = dpaa2_switch_port_mdb_add(netdev,
186562306a36Sopenharmony_ci						SWITCHDEV_OBJ_PORT_MDB(obj));
186662306a36Sopenharmony_ci		break;
186762306a36Sopenharmony_ci	default:
186862306a36Sopenharmony_ci		err = -EOPNOTSUPP;
186962306a36Sopenharmony_ci		break;
187062306a36Sopenharmony_ci	}
187162306a36Sopenharmony_ci
187262306a36Sopenharmony_ci	return err;
187362306a36Sopenharmony_ci}
187462306a36Sopenharmony_ci
187562306a36Sopenharmony_cistatic int dpaa2_switch_port_del_vlan(struct ethsw_port_priv *port_priv, u16 vid)
187662306a36Sopenharmony_ci{
187762306a36Sopenharmony_ci	struct ethsw_core *ethsw = port_priv->ethsw_data;
187862306a36Sopenharmony_ci	struct net_device *netdev = port_priv->netdev;
187962306a36Sopenharmony_ci	struct dpsw_vlan_if_cfg vcfg;
188062306a36Sopenharmony_ci	int i, err;
188162306a36Sopenharmony_ci
188262306a36Sopenharmony_ci	if (!port_priv->vlans[vid])
188362306a36Sopenharmony_ci		return -ENOENT;
188462306a36Sopenharmony_ci
188562306a36Sopenharmony_ci	if (port_priv->vlans[vid] & ETHSW_VLAN_PVID) {
188662306a36Sopenharmony_ci		/* If we are deleting the PVID of a port, use VLAN 4095 instead
188762306a36Sopenharmony_ci		 * as we are sure that neither the bridge nor the 8021q module
188862306a36Sopenharmony_ci		 * will use it
188962306a36Sopenharmony_ci		 */
189062306a36Sopenharmony_ci		err = dpaa2_switch_port_set_pvid(port_priv, 4095);
189162306a36Sopenharmony_ci		if (err)
189262306a36Sopenharmony_ci			return err;
189362306a36Sopenharmony_ci	}
189462306a36Sopenharmony_ci
189562306a36Sopenharmony_ci	vcfg.num_ifs = 1;
189662306a36Sopenharmony_ci	vcfg.if_id[0] = port_priv->idx;
189762306a36Sopenharmony_ci	if (port_priv->vlans[vid] & ETHSW_VLAN_UNTAGGED) {
189862306a36Sopenharmony_ci		err = dpsw_vlan_remove_if_untagged(ethsw->mc_io, 0,
189962306a36Sopenharmony_ci						   ethsw->dpsw_handle,
190062306a36Sopenharmony_ci						   vid, &vcfg);
190162306a36Sopenharmony_ci		if (err) {
190262306a36Sopenharmony_ci			netdev_err(netdev,
190362306a36Sopenharmony_ci				   "dpsw_vlan_remove_if_untagged err %d\n",
190462306a36Sopenharmony_ci				   err);
190562306a36Sopenharmony_ci		}
190662306a36Sopenharmony_ci		port_priv->vlans[vid] &= ~ETHSW_VLAN_UNTAGGED;
190762306a36Sopenharmony_ci	}
190862306a36Sopenharmony_ci
190962306a36Sopenharmony_ci	if (port_priv->vlans[vid] & ETHSW_VLAN_MEMBER) {
191062306a36Sopenharmony_ci		err = dpsw_vlan_remove_if(ethsw->mc_io, 0, ethsw->dpsw_handle,
191162306a36Sopenharmony_ci					  vid, &vcfg);
191262306a36Sopenharmony_ci		if (err) {
191362306a36Sopenharmony_ci			netdev_err(netdev,
191462306a36Sopenharmony_ci				   "dpsw_vlan_remove_if err %d\n", err);
191562306a36Sopenharmony_ci			return err;
191662306a36Sopenharmony_ci		}
191762306a36Sopenharmony_ci		port_priv->vlans[vid] &= ~ETHSW_VLAN_MEMBER;
191862306a36Sopenharmony_ci
191962306a36Sopenharmony_ci		/* Delete VLAN from switch if it is no longer configured on
192062306a36Sopenharmony_ci		 * any port
192162306a36Sopenharmony_ci		 */
192262306a36Sopenharmony_ci		for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
192362306a36Sopenharmony_ci			if (ethsw->ports[i] &&
192462306a36Sopenharmony_ci			    ethsw->ports[i]->vlans[vid] & ETHSW_VLAN_MEMBER)
192562306a36Sopenharmony_ci				return 0; /* Found a port member in VID */
192662306a36Sopenharmony_ci		}
192762306a36Sopenharmony_ci
192862306a36Sopenharmony_ci		ethsw->vlans[vid] &= ~ETHSW_VLAN_GLOBAL;
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_ci		err = dpaa2_switch_dellink(ethsw, vid);
193162306a36Sopenharmony_ci		if (err)
193262306a36Sopenharmony_ci			return err;
193362306a36Sopenharmony_ci	}
193462306a36Sopenharmony_ci
193562306a36Sopenharmony_ci	return 0;
193662306a36Sopenharmony_ci}
193762306a36Sopenharmony_ci
193862306a36Sopenharmony_ciint dpaa2_switch_port_vlans_del(struct net_device *netdev,
193962306a36Sopenharmony_ci				const struct switchdev_obj_port_vlan *vlan)
194062306a36Sopenharmony_ci{
194162306a36Sopenharmony_ci	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_ci	if (netif_is_bridge_master(vlan->obj.orig_dev))
194462306a36Sopenharmony_ci		return -EOPNOTSUPP;
194562306a36Sopenharmony_ci
194662306a36Sopenharmony_ci	return dpaa2_switch_port_del_vlan(port_priv, vlan->vid);
194762306a36Sopenharmony_ci}
194862306a36Sopenharmony_ci
194962306a36Sopenharmony_cistatic int dpaa2_switch_port_mdb_del(struct net_device *netdev,
195062306a36Sopenharmony_ci				     const struct switchdev_obj_port_mdb *mdb)
195162306a36Sopenharmony_ci{
195262306a36Sopenharmony_ci	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
195362306a36Sopenharmony_ci	int err;
195462306a36Sopenharmony_ci
195562306a36Sopenharmony_ci	if (!dpaa2_switch_port_lookup_address(netdev, 0, mdb->addr))
195662306a36Sopenharmony_ci		return -ENOENT;
195762306a36Sopenharmony_ci
195862306a36Sopenharmony_ci	err = dpaa2_switch_port_fdb_del_mc(port_priv, mdb->addr);
195962306a36Sopenharmony_ci	if (err)
196062306a36Sopenharmony_ci		return err;
196162306a36Sopenharmony_ci
196262306a36Sopenharmony_ci	err = dev_mc_del(netdev, mdb->addr);
196362306a36Sopenharmony_ci	if (err) {
196462306a36Sopenharmony_ci		netdev_err(netdev, "dev_mc_del err %d\n", err);
196562306a36Sopenharmony_ci		return err;
196662306a36Sopenharmony_ci	}
196762306a36Sopenharmony_ci
196862306a36Sopenharmony_ci	return err;
196962306a36Sopenharmony_ci}
197062306a36Sopenharmony_ci
197162306a36Sopenharmony_cistatic int dpaa2_switch_port_obj_del(struct net_device *netdev,
197262306a36Sopenharmony_ci				     const struct switchdev_obj *obj)
197362306a36Sopenharmony_ci{
197462306a36Sopenharmony_ci	int err;
197562306a36Sopenharmony_ci
197662306a36Sopenharmony_ci	switch (obj->id) {
197762306a36Sopenharmony_ci	case SWITCHDEV_OBJ_ID_PORT_VLAN:
197862306a36Sopenharmony_ci		err = dpaa2_switch_port_vlans_del(netdev, SWITCHDEV_OBJ_PORT_VLAN(obj));
197962306a36Sopenharmony_ci		break;
198062306a36Sopenharmony_ci	case SWITCHDEV_OBJ_ID_PORT_MDB:
198162306a36Sopenharmony_ci		err = dpaa2_switch_port_mdb_del(netdev, SWITCHDEV_OBJ_PORT_MDB(obj));
198262306a36Sopenharmony_ci		break;
198362306a36Sopenharmony_ci	default:
198462306a36Sopenharmony_ci		err = -EOPNOTSUPP;
198562306a36Sopenharmony_ci		break;
198662306a36Sopenharmony_ci	}
198762306a36Sopenharmony_ci	return err;
198862306a36Sopenharmony_ci}
198962306a36Sopenharmony_ci
199062306a36Sopenharmony_cistatic int dpaa2_switch_port_attr_set_event(struct net_device *netdev,
199162306a36Sopenharmony_ci					    struct switchdev_notifier_port_attr_info *ptr)
199262306a36Sopenharmony_ci{
199362306a36Sopenharmony_ci	int err;
199462306a36Sopenharmony_ci
199562306a36Sopenharmony_ci	err = switchdev_handle_port_attr_set(netdev, ptr,
199662306a36Sopenharmony_ci					     dpaa2_switch_port_dev_check,
199762306a36Sopenharmony_ci					     dpaa2_switch_port_attr_set);
199862306a36Sopenharmony_ci	return notifier_from_errno(err);
199962306a36Sopenharmony_ci}
200062306a36Sopenharmony_ci
200162306a36Sopenharmony_cistatic int dpaa2_switch_port_bridge_join(struct net_device *netdev,
200262306a36Sopenharmony_ci					 struct net_device *upper_dev,
200362306a36Sopenharmony_ci					 struct netlink_ext_ack *extack)
200462306a36Sopenharmony_ci{
200562306a36Sopenharmony_ci	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
200662306a36Sopenharmony_ci	struct ethsw_core *ethsw = port_priv->ethsw_data;
200762306a36Sopenharmony_ci	struct ethsw_port_priv *other_port_priv;
200862306a36Sopenharmony_ci	struct net_device *other_dev;
200962306a36Sopenharmony_ci	struct list_head *iter;
201062306a36Sopenharmony_ci	bool learn_ena;
201162306a36Sopenharmony_ci	int err;
201262306a36Sopenharmony_ci
201362306a36Sopenharmony_ci	netdev_for_each_lower_dev(upper_dev, other_dev, iter) {
201462306a36Sopenharmony_ci		if (!dpaa2_switch_port_dev_check(other_dev))
201562306a36Sopenharmony_ci			continue;
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_ci		other_port_priv = netdev_priv(other_dev);
201862306a36Sopenharmony_ci		if (other_port_priv->ethsw_data != port_priv->ethsw_data) {
201962306a36Sopenharmony_ci			NL_SET_ERR_MSG_MOD(extack,
202062306a36Sopenharmony_ci					   "Interface from a different DPSW is in the bridge already");
202162306a36Sopenharmony_ci			return -EINVAL;
202262306a36Sopenharmony_ci		}
202362306a36Sopenharmony_ci	}
202462306a36Sopenharmony_ci
202562306a36Sopenharmony_ci	/* Delete the previously manually installed VLAN 1 */
202662306a36Sopenharmony_ci	err = dpaa2_switch_port_del_vlan(port_priv, 1);
202762306a36Sopenharmony_ci	if (err)
202862306a36Sopenharmony_ci		return err;
202962306a36Sopenharmony_ci
203062306a36Sopenharmony_ci	dpaa2_switch_port_set_fdb(port_priv, upper_dev);
203162306a36Sopenharmony_ci
203262306a36Sopenharmony_ci	/* Inherit the initial bridge port learning state */
203362306a36Sopenharmony_ci	learn_ena = br_port_flag_is_set(netdev, BR_LEARNING);
203462306a36Sopenharmony_ci	err = dpaa2_switch_port_set_learning(port_priv, learn_ena);
203562306a36Sopenharmony_ci	port_priv->learn_ena = learn_ena;
203662306a36Sopenharmony_ci
203762306a36Sopenharmony_ci	/* Setup the egress flood policy (broadcast, unknown unicast) */
203862306a36Sopenharmony_ci	err = dpaa2_switch_fdb_set_egress_flood(ethsw, port_priv->fdb->fdb_id);
203962306a36Sopenharmony_ci	if (err)
204062306a36Sopenharmony_ci		goto err_egress_flood;
204162306a36Sopenharmony_ci
204262306a36Sopenharmony_ci	err = switchdev_bridge_port_offload(netdev, netdev, NULL,
204362306a36Sopenharmony_ci					    NULL, NULL, false, extack);
204462306a36Sopenharmony_ci	if (err)
204562306a36Sopenharmony_ci		goto err_switchdev_offload;
204662306a36Sopenharmony_ci
204762306a36Sopenharmony_ci	return 0;
204862306a36Sopenharmony_ci
204962306a36Sopenharmony_cierr_switchdev_offload:
205062306a36Sopenharmony_cierr_egress_flood:
205162306a36Sopenharmony_ci	dpaa2_switch_port_set_fdb(port_priv, NULL);
205262306a36Sopenharmony_ci	return err;
205362306a36Sopenharmony_ci}
205462306a36Sopenharmony_ci
205562306a36Sopenharmony_cistatic int dpaa2_switch_port_clear_rxvlan(struct net_device *vdev, int vid, void *arg)
205662306a36Sopenharmony_ci{
205762306a36Sopenharmony_ci	__be16 vlan_proto = htons(ETH_P_8021Q);
205862306a36Sopenharmony_ci
205962306a36Sopenharmony_ci	if (vdev)
206062306a36Sopenharmony_ci		vlan_proto = vlan_dev_vlan_proto(vdev);
206162306a36Sopenharmony_ci
206262306a36Sopenharmony_ci	return dpaa2_switch_port_vlan_kill(arg, vlan_proto, vid);
206362306a36Sopenharmony_ci}
206462306a36Sopenharmony_ci
206562306a36Sopenharmony_cistatic int dpaa2_switch_port_restore_rxvlan(struct net_device *vdev, int vid, void *arg)
206662306a36Sopenharmony_ci{
206762306a36Sopenharmony_ci	__be16 vlan_proto = htons(ETH_P_8021Q);
206862306a36Sopenharmony_ci
206962306a36Sopenharmony_ci	if (vdev)
207062306a36Sopenharmony_ci		vlan_proto = vlan_dev_vlan_proto(vdev);
207162306a36Sopenharmony_ci
207262306a36Sopenharmony_ci	return dpaa2_switch_port_vlan_add(arg, vlan_proto, vid);
207362306a36Sopenharmony_ci}
207462306a36Sopenharmony_ci
207562306a36Sopenharmony_cistatic void dpaa2_switch_port_pre_bridge_leave(struct net_device *netdev)
207662306a36Sopenharmony_ci{
207762306a36Sopenharmony_ci	switchdev_bridge_port_unoffload(netdev, NULL, NULL, NULL);
207862306a36Sopenharmony_ci}
207962306a36Sopenharmony_ci
208062306a36Sopenharmony_cistatic int dpaa2_switch_port_bridge_leave(struct net_device *netdev)
208162306a36Sopenharmony_ci{
208262306a36Sopenharmony_ci	struct ethsw_port_priv *port_priv = netdev_priv(netdev);
208362306a36Sopenharmony_ci	struct dpaa2_switch_fdb *old_fdb = port_priv->fdb;
208462306a36Sopenharmony_ci	struct ethsw_core *ethsw = port_priv->ethsw_data;
208562306a36Sopenharmony_ci	int err;
208662306a36Sopenharmony_ci
208762306a36Sopenharmony_ci	/* First of all, fast age any learn FDB addresses on this switch port */
208862306a36Sopenharmony_ci	dpaa2_switch_port_fast_age(port_priv);
208962306a36Sopenharmony_ci
209062306a36Sopenharmony_ci	/* Clear all RX VLANs installed through vlan_vid_add() either as VLAN
209162306a36Sopenharmony_ci	 * upper devices or otherwise from the FDB table that we are about to
209262306a36Sopenharmony_ci	 * leave
209362306a36Sopenharmony_ci	 */
209462306a36Sopenharmony_ci	err = vlan_for_each(netdev, dpaa2_switch_port_clear_rxvlan, netdev);
209562306a36Sopenharmony_ci	if (err)
209662306a36Sopenharmony_ci		netdev_err(netdev, "Unable to clear RX VLANs from old FDB table, err (%d)\n", err);
209762306a36Sopenharmony_ci
209862306a36Sopenharmony_ci	dpaa2_switch_port_set_fdb(port_priv, NULL);
209962306a36Sopenharmony_ci
210062306a36Sopenharmony_ci	/* Restore all RX VLANs into the new FDB table that we just joined */
210162306a36Sopenharmony_ci	err = vlan_for_each(netdev, dpaa2_switch_port_restore_rxvlan, netdev);
210262306a36Sopenharmony_ci	if (err)
210362306a36Sopenharmony_ci		netdev_err(netdev, "Unable to restore RX VLANs to the new FDB, err (%d)\n", err);
210462306a36Sopenharmony_ci
210562306a36Sopenharmony_ci	/* Reset the flooding state to denote that this port can send any
210662306a36Sopenharmony_ci	 * packet in standalone mode. With this, we are also ensuring that any
210762306a36Sopenharmony_ci	 * later bridge join will have the flooding flag on.
210862306a36Sopenharmony_ci	 */
210962306a36Sopenharmony_ci	port_priv->bcast_flood = true;
211062306a36Sopenharmony_ci	port_priv->ucast_flood = true;
211162306a36Sopenharmony_ci
211262306a36Sopenharmony_ci	/* Setup the egress flood policy (broadcast, unknown unicast).
211362306a36Sopenharmony_ci	 * When the port is not under a bridge, only the CTRL interface is part
211462306a36Sopenharmony_ci	 * of the flooding domain besides the actual port
211562306a36Sopenharmony_ci	 */
211662306a36Sopenharmony_ci	err = dpaa2_switch_fdb_set_egress_flood(ethsw, port_priv->fdb->fdb_id);
211762306a36Sopenharmony_ci	if (err)
211862306a36Sopenharmony_ci		return err;
211962306a36Sopenharmony_ci
212062306a36Sopenharmony_ci	/* Recreate the egress flood domain of the FDB that we just left */
212162306a36Sopenharmony_ci	err = dpaa2_switch_fdb_set_egress_flood(ethsw, old_fdb->fdb_id);
212262306a36Sopenharmony_ci	if (err)
212362306a36Sopenharmony_ci		return err;
212462306a36Sopenharmony_ci
212562306a36Sopenharmony_ci	/* No HW learning when not under a bridge */
212662306a36Sopenharmony_ci	err = dpaa2_switch_port_set_learning(port_priv, false);
212762306a36Sopenharmony_ci	if (err)
212862306a36Sopenharmony_ci		return err;
212962306a36Sopenharmony_ci	port_priv->learn_ena = false;
213062306a36Sopenharmony_ci
213162306a36Sopenharmony_ci	/* Add the VLAN 1 as PVID when not under a bridge. We need this since
213262306a36Sopenharmony_ci	 * the dpaa2 switch interfaces are not capable to be VLAN unaware
213362306a36Sopenharmony_ci	 */
213462306a36Sopenharmony_ci	return dpaa2_switch_port_add_vlan(port_priv, DEFAULT_VLAN_ID,
213562306a36Sopenharmony_ci					  BRIDGE_VLAN_INFO_UNTAGGED | BRIDGE_VLAN_INFO_PVID);
213662306a36Sopenharmony_ci}
213762306a36Sopenharmony_ci
213862306a36Sopenharmony_cistatic int dpaa2_switch_prevent_bridging_with_8021q_upper(struct net_device *netdev)
213962306a36Sopenharmony_ci{
214062306a36Sopenharmony_ci	struct net_device *upper_dev;
214162306a36Sopenharmony_ci	struct list_head *iter;
214262306a36Sopenharmony_ci
214362306a36Sopenharmony_ci	/* RCU read lock not necessary because we have write-side protection
214462306a36Sopenharmony_ci	 * (rtnl_mutex), however a non-rcu iterator does not exist.
214562306a36Sopenharmony_ci	 */
214662306a36Sopenharmony_ci	netdev_for_each_upper_dev_rcu(netdev, upper_dev, iter)
214762306a36Sopenharmony_ci		if (is_vlan_dev(upper_dev))
214862306a36Sopenharmony_ci			return -EOPNOTSUPP;
214962306a36Sopenharmony_ci
215062306a36Sopenharmony_ci	return 0;
215162306a36Sopenharmony_ci}
215262306a36Sopenharmony_ci
215362306a36Sopenharmony_cistatic int
215462306a36Sopenharmony_cidpaa2_switch_prechangeupper_sanity_checks(struct net_device *netdev,
215562306a36Sopenharmony_ci					  struct net_device *upper_dev,
215662306a36Sopenharmony_ci					  struct netlink_ext_ack *extack)
215762306a36Sopenharmony_ci{
215862306a36Sopenharmony_ci	int err;
215962306a36Sopenharmony_ci
216062306a36Sopenharmony_ci	if (!br_vlan_enabled(upper_dev)) {
216162306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Cannot join a VLAN-unaware bridge");
216262306a36Sopenharmony_ci		return -EOPNOTSUPP;
216362306a36Sopenharmony_ci	}
216462306a36Sopenharmony_ci
216562306a36Sopenharmony_ci	err = dpaa2_switch_prevent_bridging_with_8021q_upper(netdev);
216662306a36Sopenharmony_ci	if (err) {
216762306a36Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
216862306a36Sopenharmony_ci				   "Cannot join a bridge while VLAN uppers are present");
216962306a36Sopenharmony_ci		return 0;
217062306a36Sopenharmony_ci	}
217162306a36Sopenharmony_ci
217262306a36Sopenharmony_ci	return 0;
217362306a36Sopenharmony_ci}
217462306a36Sopenharmony_ci
217562306a36Sopenharmony_cistatic int dpaa2_switch_port_netdevice_event(struct notifier_block *nb,
217662306a36Sopenharmony_ci					     unsigned long event, void *ptr)
217762306a36Sopenharmony_ci{
217862306a36Sopenharmony_ci	struct net_device *netdev = netdev_notifier_info_to_dev(ptr);
217962306a36Sopenharmony_ci	struct netdev_notifier_changeupper_info *info = ptr;
218062306a36Sopenharmony_ci	struct netlink_ext_ack *extack;
218162306a36Sopenharmony_ci	struct net_device *upper_dev;
218262306a36Sopenharmony_ci	int err = 0;
218362306a36Sopenharmony_ci
218462306a36Sopenharmony_ci	if (!dpaa2_switch_port_dev_check(netdev))
218562306a36Sopenharmony_ci		return NOTIFY_DONE;
218662306a36Sopenharmony_ci
218762306a36Sopenharmony_ci	extack = netdev_notifier_info_to_extack(&info->info);
218862306a36Sopenharmony_ci
218962306a36Sopenharmony_ci	switch (event) {
219062306a36Sopenharmony_ci	case NETDEV_PRECHANGEUPPER:
219162306a36Sopenharmony_ci		upper_dev = info->upper_dev;
219262306a36Sopenharmony_ci		if (!netif_is_bridge_master(upper_dev))
219362306a36Sopenharmony_ci			break;
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_ci		err = dpaa2_switch_prechangeupper_sanity_checks(netdev,
219662306a36Sopenharmony_ci								upper_dev,
219762306a36Sopenharmony_ci								extack);
219862306a36Sopenharmony_ci		if (err)
219962306a36Sopenharmony_ci			goto out;
220062306a36Sopenharmony_ci
220162306a36Sopenharmony_ci		if (!info->linking)
220262306a36Sopenharmony_ci			dpaa2_switch_port_pre_bridge_leave(netdev);
220362306a36Sopenharmony_ci
220462306a36Sopenharmony_ci		break;
220562306a36Sopenharmony_ci	case NETDEV_CHANGEUPPER:
220662306a36Sopenharmony_ci		upper_dev = info->upper_dev;
220762306a36Sopenharmony_ci		if (netif_is_bridge_master(upper_dev)) {
220862306a36Sopenharmony_ci			if (info->linking)
220962306a36Sopenharmony_ci				err = dpaa2_switch_port_bridge_join(netdev,
221062306a36Sopenharmony_ci								    upper_dev,
221162306a36Sopenharmony_ci								    extack);
221262306a36Sopenharmony_ci			else
221362306a36Sopenharmony_ci				err = dpaa2_switch_port_bridge_leave(netdev);
221462306a36Sopenharmony_ci		}
221562306a36Sopenharmony_ci		break;
221662306a36Sopenharmony_ci	}
221762306a36Sopenharmony_ci
221862306a36Sopenharmony_ciout:
221962306a36Sopenharmony_ci	return notifier_from_errno(err);
222062306a36Sopenharmony_ci}
222162306a36Sopenharmony_ci
222262306a36Sopenharmony_cistruct ethsw_switchdev_event_work {
222362306a36Sopenharmony_ci	struct work_struct work;
222462306a36Sopenharmony_ci	struct switchdev_notifier_fdb_info fdb_info;
222562306a36Sopenharmony_ci	struct net_device *dev;
222662306a36Sopenharmony_ci	unsigned long event;
222762306a36Sopenharmony_ci};
222862306a36Sopenharmony_ci
222962306a36Sopenharmony_cistatic void dpaa2_switch_event_work(struct work_struct *work)
223062306a36Sopenharmony_ci{
223162306a36Sopenharmony_ci	struct ethsw_switchdev_event_work *switchdev_work =
223262306a36Sopenharmony_ci		container_of(work, struct ethsw_switchdev_event_work, work);
223362306a36Sopenharmony_ci	struct net_device *dev = switchdev_work->dev;
223462306a36Sopenharmony_ci	struct switchdev_notifier_fdb_info *fdb_info;
223562306a36Sopenharmony_ci	int err;
223662306a36Sopenharmony_ci
223762306a36Sopenharmony_ci	rtnl_lock();
223862306a36Sopenharmony_ci	fdb_info = &switchdev_work->fdb_info;
223962306a36Sopenharmony_ci
224062306a36Sopenharmony_ci	switch (switchdev_work->event) {
224162306a36Sopenharmony_ci	case SWITCHDEV_FDB_ADD_TO_DEVICE:
224262306a36Sopenharmony_ci		if (!fdb_info->added_by_user || fdb_info->is_local)
224362306a36Sopenharmony_ci			break;
224462306a36Sopenharmony_ci		if (is_unicast_ether_addr(fdb_info->addr))
224562306a36Sopenharmony_ci			err = dpaa2_switch_port_fdb_add_uc(netdev_priv(dev),
224662306a36Sopenharmony_ci							   fdb_info->addr);
224762306a36Sopenharmony_ci		else
224862306a36Sopenharmony_ci			err = dpaa2_switch_port_fdb_add_mc(netdev_priv(dev),
224962306a36Sopenharmony_ci							   fdb_info->addr);
225062306a36Sopenharmony_ci		if (err)
225162306a36Sopenharmony_ci			break;
225262306a36Sopenharmony_ci		fdb_info->offloaded = true;
225362306a36Sopenharmony_ci		call_switchdev_notifiers(SWITCHDEV_FDB_OFFLOADED, dev,
225462306a36Sopenharmony_ci					 &fdb_info->info, NULL);
225562306a36Sopenharmony_ci		break;
225662306a36Sopenharmony_ci	case SWITCHDEV_FDB_DEL_TO_DEVICE:
225762306a36Sopenharmony_ci		if (!fdb_info->added_by_user || fdb_info->is_local)
225862306a36Sopenharmony_ci			break;
225962306a36Sopenharmony_ci		if (is_unicast_ether_addr(fdb_info->addr))
226062306a36Sopenharmony_ci			dpaa2_switch_port_fdb_del_uc(netdev_priv(dev), fdb_info->addr);
226162306a36Sopenharmony_ci		else
226262306a36Sopenharmony_ci			dpaa2_switch_port_fdb_del_mc(netdev_priv(dev), fdb_info->addr);
226362306a36Sopenharmony_ci		break;
226462306a36Sopenharmony_ci	}
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_ci	rtnl_unlock();
226762306a36Sopenharmony_ci	kfree(switchdev_work->fdb_info.addr);
226862306a36Sopenharmony_ci	kfree(switchdev_work);
226962306a36Sopenharmony_ci	dev_put(dev);
227062306a36Sopenharmony_ci}
227162306a36Sopenharmony_ci
227262306a36Sopenharmony_ci/* Called under rcu_read_lock() */
227362306a36Sopenharmony_cistatic int dpaa2_switch_port_event(struct notifier_block *nb,
227462306a36Sopenharmony_ci				   unsigned long event, void *ptr)
227562306a36Sopenharmony_ci{
227662306a36Sopenharmony_ci	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
227762306a36Sopenharmony_ci	struct ethsw_port_priv *port_priv = netdev_priv(dev);
227862306a36Sopenharmony_ci	struct ethsw_switchdev_event_work *switchdev_work;
227962306a36Sopenharmony_ci	struct switchdev_notifier_fdb_info *fdb_info = ptr;
228062306a36Sopenharmony_ci	struct ethsw_core *ethsw = port_priv->ethsw_data;
228162306a36Sopenharmony_ci
228262306a36Sopenharmony_ci	if (event == SWITCHDEV_PORT_ATTR_SET)
228362306a36Sopenharmony_ci		return dpaa2_switch_port_attr_set_event(dev, ptr);
228462306a36Sopenharmony_ci
228562306a36Sopenharmony_ci	if (!dpaa2_switch_port_dev_check(dev))
228662306a36Sopenharmony_ci		return NOTIFY_DONE;
228762306a36Sopenharmony_ci
228862306a36Sopenharmony_ci	switchdev_work = kzalloc(sizeof(*switchdev_work), GFP_ATOMIC);
228962306a36Sopenharmony_ci	if (!switchdev_work)
229062306a36Sopenharmony_ci		return NOTIFY_BAD;
229162306a36Sopenharmony_ci
229262306a36Sopenharmony_ci	INIT_WORK(&switchdev_work->work, dpaa2_switch_event_work);
229362306a36Sopenharmony_ci	switchdev_work->dev = dev;
229462306a36Sopenharmony_ci	switchdev_work->event = event;
229562306a36Sopenharmony_ci
229662306a36Sopenharmony_ci	switch (event) {
229762306a36Sopenharmony_ci	case SWITCHDEV_FDB_ADD_TO_DEVICE:
229862306a36Sopenharmony_ci	case SWITCHDEV_FDB_DEL_TO_DEVICE:
229962306a36Sopenharmony_ci		memcpy(&switchdev_work->fdb_info, ptr,
230062306a36Sopenharmony_ci		       sizeof(switchdev_work->fdb_info));
230162306a36Sopenharmony_ci		switchdev_work->fdb_info.addr = kzalloc(ETH_ALEN, GFP_ATOMIC);
230262306a36Sopenharmony_ci		if (!switchdev_work->fdb_info.addr)
230362306a36Sopenharmony_ci			goto err_addr_alloc;
230462306a36Sopenharmony_ci
230562306a36Sopenharmony_ci		ether_addr_copy((u8 *)switchdev_work->fdb_info.addr,
230662306a36Sopenharmony_ci				fdb_info->addr);
230762306a36Sopenharmony_ci
230862306a36Sopenharmony_ci		/* Take a reference on the device to avoid being freed. */
230962306a36Sopenharmony_ci		dev_hold(dev);
231062306a36Sopenharmony_ci		break;
231162306a36Sopenharmony_ci	default:
231262306a36Sopenharmony_ci		kfree(switchdev_work);
231362306a36Sopenharmony_ci		return NOTIFY_DONE;
231462306a36Sopenharmony_ci	}
231562306a36Sopenharmony_ci
231662306a36Sopenharmony_ci	queue_work(ethsw->workqueue, &switchdev_work->work);
231762306a36Sopenharmony_ci
231862306a36Sopenharmony_ci	return NOTIFY_DONE;
231962306a36Sopenharmony_ci
232062306a36Sopenharmony_cierr_addr_alloc:
232162306a36Sopenharmony_ci	kfree(switchdev_work);
232262306a36Sopenharmony_ci	return NOTIFY_BAD;
232362306a36Sopenharmony_ci}
232462306a36Sopenharmony_ci
232562306a36Sopenharmony_cistatic int dpaa2_switch_port_obj_event(unsigned long event,
232662306a36Sopenharmony_ci				       struct net_device *netdev,
232762306a36Sopenharmony_ci				       struct switchdev_notifier_port_obj_info *port_obj_info)
232862306a36Sopenharmony_ci{
232962306a36Sopenharmony_ci	int err = -EOPNOTSUPP;
233062306a36Sopenharmony_ci
233162306a36Sopenharmony_ci	if (!dpaa2_switch_port_dev_check(netdev))
233262306a36Sopenharmony_ci		return NOTIFY_DONE;
233362306a36Sopenharmony_ci
233462306a36Sopenharmony_ci	switch (event) {
233562306a36Sopenharmony_ci	case SWITCHDEV_PORT_OBJ_ADD:
233662306a36Sopenharmony_ci		err = dpaa2_switch_port_obj_add(netdev, port_obj_info->obj);
233762306a36Sopenharmony_ci		break;
233862306a36Sopenharmony_ci	case SWITCHDEV_PORT_OBJ_DEL:
233962306a36Sopenharmony_ci		err = dpaa2_switch_port_obj_del(netdev, port_obj_info->obj);
234062306a36Sopenharmony_ci		break;
234162306a36Sopenharmony_ci	}
234262306a36Sopenharmony_ci
234362306a36Sopenharmony_ci	port_obj_info->handled = true;
234462306a36Sopenharmony_ci	return notifier_from_errno(err);
234562306a36Sopenharmony_ci}
234662306a36Sopenharmony_ci
234762306a36Sopenharmony_cistatic int dpaa2_switch_port_blocking_event(struct notifier_block *nb,
234862306a36Sopenharmony_ci					    unsigned long event, void *ptr)
234962306a36Sopenharmony_ci{
235062306a36Sopenharmony_ci	struct net_device *dev = switchdev_notifier_info_to_dev(ptr);
235162306a36Sopenharmony_ci
235262306a36Sopenharmony_ci	switch (event) {
235362306a36Sopenharmony_ci	case SWITCHDEV_PORT_OBJ_ADD:
235462306a36Sopenharmony_ci	case SWITCHDEV_PORT_OBJ_DEL:
235562306a36Sopenharmony_ci		return dpaa2_switch_port_obj_event(event, dev, ptr);
235662306a36Sopenharmony_ci	case SWITCHDEV_PORT_ATTR_SET:
235762306a36Sopenharmony_ci		return dpaa2_switch_port_attr_set_event(dev, ptr);
235862306a36Sopenharmony_ci	}
235962306a36Sopenharmony_ci
236062306a36Sopenharmony_ci	return NOTIFY_DONE;
236162306a36Sopenharmony_ci}
236262306a36Sopenharmony_ci
236362306a36Sopenharmony_ci/* Build a linear skb based on a single-buffer frame descriptor */
236462306a36Sopenharmony_cistatic struct sk_buff *dpaa2_switch_build_linear_skb(struct ethsw_core *ethsw,
236562306a36Sopenharmony_ci						     const struct dpaa2_fd *fd)
236662306a36Sopenharmony_ci{
236762306a36Sopenharmony_ci	u16 fd_offset = dpaa2_fd_get_offset(fd);
236862306a36Sopenharmony_ci	dma_addr_t addr = dpaa2_fd_get_addr(fd);
236962306a36Sopenharmony_ci	u32 fd_length = dpaa2_fd_get_len(fd);
237062306a36Sopenharmony_ci	struct device *dev = ethsw->dev;
237162306a36Sopenharmony_ci	struct sk_buff *skb = NULL;
237262306a36Sopenharmony_ci	void *fd_vaddr;
237362306a36Sopenharmony_ci
237462306a36Sopenharmony_ci	fd_vaddr = dpaa2_iova_to_virt(ethsw->iommu_domain, addr);
237562306a36Sopenharmony_ci	dma_unmap_page(dev, addr, DPAA2_SWITCH_RX_BUF_SIZE,
237662306a36Sopenharmony_ci		       DMA_FROM_DEVICE);
237762306a36Sopenharmony_ci
237862306a36Sopenharmony_ci	skb = build_skb(fd_vaddr, DPAA2_SWITCH_RX_BUF_SIZE +
237962306a36Sopenharmony_ci			SKB_DATA_ALIGN(sizeof(struct skb_shared_info)));
238062306a36Sopenharmony_ci	if (unlikely(!skb)) {
238162306a36Sopenharmony_ci		dev_err(dev, "build_skb() failed\n");
238262306a36Sopenharmony_ci		return NULL;
238362306a36Sopenharmony_ci	}
238462306a36Sopenharmony_ci
238562306a36Sopenharmony_ci	skb_reserve(skb, fd_offset);
238662306a36Sopenharmony_ci	skb_put(skb, fd_length);
238762306a36Sopenharmony_ci
238862306a36Sopenharmony_ci	ethsw->buf_count--;
238962306a36Sopenharmony_ci
239062306a36Sopenharmony_ci	return skb;
239162306a36Sopenharmony_ci}
239262306a36Sopenharmony_ci
239362306a36Sopenharmony_cistatic void dpaa2_switch_tx_conf(struct dpaa2_switch_fq *fq,
239462306a36Sopenharmony_ci				 const struct dpaa2_fd *fd)
239562306a36Sopenharmony_ci{
239662306a36Sopenharmony_ci	dpaa2_switch_free_fd(fq->ethsw, fd);
239762306a36Sopenharmony_ci}
239862306a36Sopenharmony_ci
239962306a36Sopenharmony_cistatic void dpaa2_switch_rx(struct dpaa2_switch_fq *fq,
240062306a36Sopenharmony_ci			    const struct dpaa2_fd *fd)
240162306a36Sopenharmony_ci{
240262306a36Sopenharmony_ci	struct ethsw_core *ethsw = fq->ethsw;
240362306a36Sopenharmony_ci	struct ethsw_port_priv *port_priv;
240462306a36Sopenharmony_ci	struct net_device *netdev;
240562306a36Sopenharmony_ci	struct vlan_ethhdr *hdr;
240662306a36Sopenharmony_ci	struct sk_buff *skb;
240762306a36Sopenharmony_ci	u16 vlan_tci, vid;
240862306a36Sopenharmony_ci	int if_id, err;
240962306a36Sopenharmony_ci
241062306a36Sopenharmony_ci	/* get switch ingress interface ID */
241162306a36Sopenharmony_ci	if_id = upper_32_bits(dpaa2_fd_get_flc(fd)) & 0x0000FFFF;
241262306a36Sopenharmony_ci
241362306a36Sopenharmony_ci	if (if_id >= ethsw->sw_attr.num_ifs) {
241462306a36Sopenharmony_ci		dev_err(ethsw->dev, "Frame received from unknown interface!\n");
241562306a36Sopenharmony_ci		goto err_free_fd;
241662306a36Sopenharmony_ci	}
241762306a36Sopenharmony_ci	port_priv = ethsw->ports[if_id];
241862306a36Sopenharmony_ci	netdev = port_priv->netdev;
241962306a36Sopenharmony_ci
242062306a36Sopenharmony_ci	/* build the SKB based on the FD received */
242162306a36Sopenharmony_ci	if (dpaa2_fd_get_format(fd) != dpaa2_fd_single) {
242262306a36Sopenharmony_ci		if (net_ratelimit()) {
242362306a36Sopenharmony_ci			netdev_err(netdev, "Received invalid frame format\n");
242462306a36Sopenharmony_ci			goto err_free_fd;
242562306a36Sopenharmony_ci		}
242662306a36Sopenharmony_ci	}
242762306a36Sopenharmony_ci
242862306a36Sopenharmony_ci	skb = dpaa2_switch_build_linear_skb(ethsw, fd);
242962306a36Sopenharmony_ci	if (unlikely(!skb))
243062306a36Sopenharmony_ci		goto err_free_fd;
243162306a36Sopenharmony_ci
243262306a36Sopenharmony_ci	skb_reset_mac_header(skb);
243362306a36Sopenharmony_ci
243462306a36Sopenharmony_ci	/* Remove the VLAN header if the packet that we just received has a vid
243562306a36Sopenharmony_ci	 * equal to the port PVIDs. Since the dpaa2-switch can operate only in
243662306a36Sopenharmony_ci	 * VLAN-aware mode and no alterations are made on the packet when it's
243762306a36Sopenharmony_ci	 * redirected/mirrored to the control interface, we are sure that there
243862306a36Sopenharmony_ci	 * will always be a VLAN header present.
243962306a36Sopenharmony_ci	 */
244062306a36Sopenharmony_ci	hdr = vlan_eth_hdr(skb);
244162306a36Sopenharmony_ci	vid = ntohs(hdr->h_vlan_TCI) & VLAN_VID_MASK;
244262306a36Sopenharmony_ci	if (vid == port_priv->pvid) {
244362306a36Sopenharmony_ci		err = __skb_vlan_pop(skb, &vlan_tci);
244462306a36Sopenharmony_ci		if (err) {
244562306a36Sopenharmony_ci			dev_info(ethsw->dev, "__skb_vlan_pop() returned %d", err);
244662306a36Sopenharmony_ci			goto err_free_fd;
244762306a36Sopenharmony_ci		}
244862306a36Sopenharmony_ci	}
244962306a36Sopenharmony_ci
245062306a36Sopenharmony_ci	skb->dev = netdev;
245162306a36Sopenharmony_ci	skb->protocol = eth_type_trans(skb, skb->dev);
245262306a36Sopenharmony_ci
245362306a36Sopenharmony_ci	/* Setup the offload_fwd_mark only if the port is under a bridge */
245462306a36Sopenharmony_ci	skb->offload_fwd_mark = !!(port_priv->fdb->bridge_dev);
245562306a36Sopenharmony_ci
245662306a36Sopenharmony_ci	netif_receive_skb(skb);
245762306a36Sopenharmony_ci
245862306a36Sopenharmony_ci	return;
245962306a36Sopenharmony_ci
246062306a36Sopenharmony_cierr_free_fd:
246162306a36Sopenharmony_ci	dpaa2_switch_free_fd(ethsw, fd);
246262306a36Sopenharmony_ci}
246362306a36Sopenharmony_ci
246462306a36Sopenharmony_cistatic void dpaa2_switch_detect_features(struct ethsw_core *ethsw)
246562306a36Sopenharmony_ci{
246662306a36Sopenharmony_ci	ethsw->features = 0;
246762306a36Sopenharmony_ci
246862306a36Sopenharmony_ci	if (ethsw->major > 8 || (ethsw->major == 8 && ethsw->minor >= 6))
246962306a36Sopenharmony_ci		ethsw->features |= ETHSW_FEATURE_MAC_ADDR;
247062306a36Sopenharmony_ci}
247162306a36Sopenharmony_ci
247262306a36Sopenharmony_cistatic int dpaa2_switch_setup_fqs(struct ethsw_core *ethsw)
247362306a36Sopenharmony_ci{
247462306a36Sopenharmony_ci	struct dpsw_ctrl_if_attr ctrl_if_attr;
247562306a36Sopenharmony_ci	struct device *dev = ethsw->dev;
247662306a36Sopenharmony_ci	int i = 0;
247762306a36Sopenharmony_ci	int err;
247862306a36Sopenharmony_ci
247962306a36Sopenharmony_ci	err = dpsw_ctrl_if_get_attributes(ethsw->mc_io, 0, ethsw->dpsw_handle,
248062306a36Sopenharmony_ci					  &ctrl_if_attr);
248162306a36Sopenharmony_ci	if (err) {
248262306a36Sopenharmony_ci		dev_err(dev, "dpsw_ctrl_if_get_attributes() = %d\n", err);
248362306a36Sopenharmony_ci		return err;
248462306a36Sopenharmony_ci	}
248562306a36Sopenharmony_ci
248662306a36Sopenharmony_ci	ethsw->fq[i].fqid = ctrl_if_attr.rx_fqid;
248762306a36Sopenharmony_ci	ethsw->fq[i].ethsw = ethsw;
248862306a36Sopenharmony_ci	ethsw->fq[i++].type = DPSW_QUEUE_RX;
248962306a36Sopenharmony_ci
249062306a36Sopenharmony_ci	ethsw->fq[i].fqid = ctrl_if_attr.tx_err_conf_fqid;
249162306a36Sopenharmony_ci	ethsw->fq[i].ethsw = ethsw;
249262306a36Sopenharmony_ci	ethsw->fq[i++].type = DPSW_QUEUE_TX_ERR_CONF;
249362306a36Sopenharmony_ci
249462306a36Sopenharmony_ci	return 0;
249562306a36Sopenharmony_ci}
249662306a36Sopenharmony_ci
249762306a36Sopenharmony_ci/* Free buffers acquired from the buffer pool or which were meant to
249862306a36Sopenharmony_ci * be released in the pool
249962306a36Sopenharmony_ci */
250062306a36Sopenharmony_cistatic void dpaa2_switch_free_bufs(struct ethsw_core *ethsw, u64 *buf_array, int count)
250162306a36Sopenharmony_ci{
250262306a36Sopenharmony_ci	struct device *dev = ethsw->dev;
250362306a36Sopenharmony_ci	void *vaddr;
250462306a36Sopenharmony_ci	int i;
250562306a36Sopenharmony_ci
250662306a36Sopenharmony_ci	for (i = 0; i < count; i++) {
250762306a36Sopenharmony_ci		vaddr = dpaa2_iova_to_virt(ethsw->iommu_domain, buf_array[i]);
250862306a36Sopenharmony_ci		dma_unmap_page(dev, buf_array[i], DPAA2_SWITCH_RX_BUF_SIZE,
250962306a36Sopenharmony_ci			       DMA_FROM_DEVICE);
251062306a36Sopenharmony_ci		free_pages((unsigned long)vaddr, 0);
251162306a36Sopenharmony_ci	}
251262306a36Sopenharmony_ci}
251362306a36Sopenharmony_ci
251462306a36Sopenharmony_ci/* Perform a single release command to add buffers
251562306a36Sopenharmony_ci * to the specified buffer pool
251662306a36Sopenharmony_ci */
251762306a36Sopenharmony_cistatic int dpaa2_switch_add_bufs(struct ethsw_core *ethsw, u16 bpid)
251862306a36Sopenharmony_ci{
251962306a36Sopenharmony_ci	struct device *dev = ethsw->dev;
252062306a36Sopenharmony_ci	u64 buf_array[BUFS_PER_CMD];
252162306a36Sopenharmony_ci	struct page *page;
252262306a36Sopenharmony_ci	int retries = 0;
252362306a36Sopenharmony_ci	dma_addr_t addr;
252462306a36Sopenharmony_ci	int err;
252562306a36Sopenharmony_ci	int i;
252662306a36Sopenharmony_ci
252762306a36Sopenharmony_ci	for (i = 0; i < BUFS_PER_CMD; i++) {
252862306a36Sopenharmony_ci		/* Allocate one page for each Rx buffer. WRIOP sees
252962306a36Sopenharmony_ci		 * the entire page except for a tailroom reserved for
253062306a36Sopenharmony_ci		 * skb shared info
253162306a36Sopenharmony_ci		 */
253262306a36Sopenharmony_ci		page = dev_alloc_pages(0);
253362306a36Sopenharmony_ci		if (!page) {
253462306a36Sopenharmony_ci			dev_err(dev, "buffer allocation failed\n");
253562306a36Sopenharmony_ci			goto err_alloc;
253662306a36Sopenharmony_ci		}
253762306a36Sopenharmony_ci
253862306a36Sopenharmony_ci		addr = dma_map_page(dev, page, 0, DPAA2_SWITCH_RX_BUF_SIZE,
253962306a36Sopenharmony_ci				    DMA_FROM_DEVICE);
254062306a36Sopenharmony_ci		if (dma_mapping_error(dev, addr)) {
254162306a36Sopenharmony_ci			dev_err(dev, "dma_map_single() failed\n");
254262306a36Sopenharmony_ci			goto err_map;
254362306a36Sopenharmony_ci		}
254462306a36Sopenharmony_ci		buf_array[i] = addr;
254562306a36Sopenharmony_ci	}
254662306a36Sopenharmony_ci
254762306a36Sopenharmony_cirelease_bufs:
254862306a36Sopenharmony_ci	/* In case the portal is busy, retry until successful or
254962306a36Sopenharmony_ci	 * max retries hit.
255062306a36Sopenharmony_ci	 */
255162306a36Sopenharmony_ci	while ((err = dpaa2_io_service_release(NULL, bpid,
255262306a36Sopenharmony_ci					       buf_array, i)) == -EBUSY) {
255362306a36Sopenharmony_ci		if (retries++ >= DPAA2_SWITCH_SWP_BUSY_RETRIES)
255462306a36Sopenharmony_ci			break;
255562306a36Sopenharmony_ci
255662306a36Sopenharmony_ci		cpu_relax();
255762306a36Sopenharmony_ci	}
255862306a36Sopenharmony_ci
255962306a36Sopenharmony_ci	/* If release command failed, clean up and bail out. */
256062306a36Sopenharmony_ci	if (err) {
256162306a36Sopenharmony_ci		dpaa2_switch_free_bufs(ethsw, buf_array, i);
256262306a36Sopenharmony_ci		return 0;
256362306a36Sopenharmony_ci	}
256462306a36Sopenharmony_ci
256562306a36Sopenharmony_ci	return i;
256662306a36Sopenharmony_ci
256762306a36Sopenharmony_cierr_map:
256862306a36Sopenharmony_ci	__free_pages(page, 0);
256962306a36Sopenharmony_cierr_alloc:
257062306a36Sopenharmony_ci	/* If we managed to allocate at least some buffers,
257162306a36Sopenharmony_ci	 * release them to hardware
257262306a36Sopenharmony_ci	 */
257362306a36Sopenharmony_ci	if (i)
257462306a36Sopenharmony_ci		goto release_bufs;
257562306a36Sopenharmony_ci
257662306a36Sopenharmony_ci	return 0;
257762306a36Sopenharmony_ci}
257862306a36Sopenharmony_ci
257962306a36Sopenharmony_cistatic int dpaa2_switch_refill_bp(struct ethsw_core *ethsw)
258062306a36Sopenharmony_ci{
258162306a36Sopenharmony_ci	int *count = &ethsw->buf_count;
258262306a36Sopenharmony_ci	int new_count;
258362306a36Sopenharmony_ci	int err = 0;
258462306a36Sopenharmony_ci
258562306a36Sopenharmony_ci	if (unlikely(*count < DPAA2_ETHSW_REFILL_THRESH)) {
258662306a36Sopenharmony_ci		do {
258762306a36Sopenharmony_ci			new_count = dpaa2_switch_add_bufs(ethsw, ethsw->bpid);
258862306a36Sopenharmony_ci			if (unlikely(!new_count)) {
258962306a36Sopenharmony_ci				/* Out of memory; abort for now, we'll
259062306a36Sopenharmony_ci				 * try later on
259162306a36Sopenharmony_ci				 */
259262306a36Sopenharmony_ci				break;
259362306a36Sopenharmony_ci			}
259462306a36Sopenharmony_ci			*count += new_count;
259562306a36Sopenharmony_ci		} while (*count < DPAA2_ETHSW_NUM_BUFS);
259662306a36Sopenharmony_ci
259762306a36Sopenharmony_ci		if (unlikely(*count < DPAA2_ETHSW_NUM_BUFS))
259862306a36Sopenharmony_ci			err = -ENOMEM;
259962306a36Sopenharmony_ci	}
260062306a36Sopenharmony_ci
260162306a36Sopenharmony_ci	return err;
260262306a36Sopenharmony_ci}
260362306a36Sopenharmony_ci
260462306a36Sopenharmony_cistatic int dpaa2_switch_seed_bp(struct ethsw_core *ethsw)
260562306a36Sopenharmony_ci{
260662306a36Sopenharmony_ci	int *count, i;
260762306a36Sopenharmony_ci
260862306a36Sopenharmony_ci	for (i = 0; i < DPAA2_ETHSW_NUM_BUFS; i += BUFS_PER_CMD) {
260962306a36Sopenharmony_ci		count = &ethsw->buf_count;
261062306a36Sopenharmony_ci		*count += dpaa2_switch_add_bufs(ethsw, ethsw->bpid);
261162306a36Sopenharmony_ci
261262306a36Sopenharmony_ci		if (unlikely(*count < BUFS_PER_CMD))
261362306a36Sopenharmony_ci			return -ENOMEM;
261462306a36Sopenharmony_ci	}
261562306a36Sopenharmony_ci
261662306a36Sopenharmony_ci	return 0;
261762306a36Sopenharmony_ci}
261862306a36Sopenharmony_ci
261962306a36Sopenharmony_cistatic void dpaa2_switch_drain_bp(struct ethsw_core *ethsw)
262062306a36Sopenharmony_ci{
262162306a36Sopenharmony_ci	u64 buf_array[BUFS_PER_CMD];
262262306a36Sopenharmony_ci	int ret;
262362306a36Sopenharmony_ci
262462306a36Sopenharmony_ci	do {
262562306a36Sopenharmony_ci		ret = dpaa2_io_service_acquire(NULL, ethsw->bpid,
262662306a36Sopenharmony_ci					       buf_array, BUFS_PER_CMD);
262762306a36Sopenharmony_ci		if (ret < 0) {
262862306a36Sopenharmony_ci			dev_err(ethsw->dev,
262962306a36Sopenharmony_ci				"dpaa2_io_service_acquire() = %d\n", ret);
263062306a36Sopenharmony_ci			return;
263162306a36Sopenharmony_ci		}
263262306a36Sopenharmony_ci		dpaa2_switch_free_bufs(ethsw, buf_array, ret);
263362306a36Sopenharmony_ci
263462306a36Sopenharmony_ci	} while (ret);
263562306a36Sopenharmony_ci}
263662306a36Sopenharmony_ci
263762306a36Sopenharmony_cistatic int dpaa2_switch_setup_dpbp(struct ethsw_core *ethsw)
263862306a36Sopenharmony_ci{
263962306a36Sopenharmony_ci	struct dpsw_ctrl_if_pools_cfg dpsw_ctrl_if_pools_cfg = { 0 };
264062306a36Sopenharmony_ci	struct device *dev = ethsw->dev;
264162306a36Sopenharmony_ci	struct fsl_mc_device *dpbp_dev;
264262306a36Sopenharmony_ci	struct dpbp_attr dpbp_attrs;
264362306a36Sopenharmony_ci	int err;
264462306a36Sopenharmony_ci
264562306a36Sopenharmony_ci	err = fsl_mc_object_allocate(to_fsl_mc_device(dev), FSL_MC_POOL_DPBP,
264662306a36Sopenharmony_ci				     &dpbp_dev);
264762306a36Sopenharmony_ci	if (err) {
264862306a36Sopenharmony_ci		if (err == -ENXIO)
264962306a36Sopenharmony_ci			err = -EPROBE_DEFER;
265062306a36Sopenharmony_ci		else
265162306a36Sopenharmony_ci			dev_err(dev, "DPBP device allocation failed\n");
265262306a36Sopenharmony_ci		return err;
265362306a36Sopenharmony_ci	}
265462306a36Sopenharmony_ci	ethsw->dpbp_dev = dpbp_dev;
265562306a36Sopenharmony_ci
265662306a36Sopenharmony_ci	err = dpbp_open(ethsw->mc_io, 0, dpbp_dev->obj_desc.id,
265762306a36Sopenharmony_ci			&dpbp_dev->mc_handle);
265862306a36Sopenharmony_ci	if (err) {
265962306a36Sopenharmony_ci		dev_err(dev, "dpbp_open() failed\n");
266062306a36Sopenharmony_ci		goto err_open;
266162306a36Sopenharmony_ci	}
266262306a36Sopenharmony_ci
266362306a36Sopenharmony_ci	err = dpbp_reset(ethsw->mc_io, 0, dpbp_dev->mc_handle);
266462306a36Sopenharmony_ci	if (err) {
266562306a36Sopenharmony_ci		dev_err(dev, "dpbp_reset() failed\n");
266662306a36Sopenharmony_ci		goto err_reset;
266762306a36Sopenharmony_ci	}
266862306a36Sopenharmony_ci
266962306a36Sopenharmony_ci	err = dpbp_enable(ethsw->mc_io, 0, dpbp_dev->mc_handle);
267062306a36Sopenharmony_ci	if (err) {
267162306a36Sopenharmony_ci		dev_err(dev, "dpbp_enable() failed\n");
267262306a36Sopenharmony_ci		goto err_enable;
267362306a36Sopenharmony_ci	}
267462306a36Sopenharmony_ci
267562306a36Sopenharmony_ci	err = dpbp_get_attributes(ethsw->mc_io, 0, dpbp_dev->mc_handle,
267662306a36Sopenharmony_ci				  &dpbp_attrs);
267762306a36Sopenharmony_ci	if (err) {
267862306a36Sopenharmony_ci		dev_err(dev, "dpbp_get_attributes() failed\n");
267962306a36Sopenharmony_ci		goto err_get_attr;
268062306a36Sopenharmony_ci	}
268162306a36Sopenharmony_ci
268262306a36Sopenharmony_ci	dpsw_ctrl_if_pools_cfg.num_dpbp = 1;
268362306a36Sopenharmony_ci	dpsw_ctrl_if_pools_cfg.pools[0].dpbp_id = dpbp_attrs.id;
268462306a36Sopenharmony_ci	dpsw_ctrl_if_pools_cfg.pools[0].buffer_size = DPAA2_SWITCH_RX_BUF_SIZE;
268562306a36Sopenharmony_ci	dpsw_ctrl_if_pools_cfg.pools[0].backup_pool = 0;
268662306a36Sopenharmony_ci
268762306a36Sopenharmony_ci	err = dpsw_ctrl_if_set_pools(ethsw->mc_io, 0, ethsw->dpsw_handle,
268862306a36Sopenharmony_ci				     &dpsw_ctrl_if_pools_cfg);
268962306a36Sopenharmony_ci	if (err) {
269062306a36Sopenharmony_ci		dev_err(dev, "dpsw_ctrl_if_set_pools() failed\n");
269162306a36Sopenharmony_ci		goto err_get_attr;
269262306a36Sopenharmony_ci	}
269362306a36Sopenharmony_ci	ethsw->bpid = dpbp_attrs.id;
269462306a36Sopenharmony_ci
269562306a36Sopenharmony_ci	return 0;
269662306a36Sopenharmony_ci
269762306a36Sopenharmony_cierr_get_attr:
269862306a36Sopenharmony_ci	dpbp_disable(ethsw->mc_io, 0, dpbp_dev->mc_handle);
269962306a36Sopenharmony_cierr_enable:
270062306a36Sopenharmony_cierr_reset:
270162306a36Sopenharmony_ci	dpbp_close(ethsw->mc_io, 0, dpbp_dev->mc_handle);
270262306a36Sopenharmony_cierr_open:
270362306a36Sopenharmony_ci	fsl_mc_object_free(dpbp_dev);
270462306a36Sopenharmony_ci	return err;
270562306a36Sopenharmony_ci}
270662306a36Sopenharmony_ci
270762306a36Sopenharmony_cistatic void dpaa2_switch_free_dpbp(struct ethsw_core *ethsw)
270862306a36Sopenharmony_ci{
270962306a36Sopenharmony_ci	dpbp_disable(ethsw->mc_io, 0, ethsw->dpbp_dev->mc_handle);
271062306a36Sopenharmony_ci	dpbp_close(ethsw->mc_io, 0, ethsw->dpbp_dev->mc_handle);
271162306a36Sopenharmony_ci	fsl_mc_object_free(ethsw->dpbp_dev);
271262306a36Sopenharmony_ci}
271362306a36Sopenharmony_ci
271462306a36Sopenharmony_cistatic int dpaa2_switch_alloc_rings(struct ethsw_core *ethsw)
271562306a36Sopenharmony_ci{
271662306a36Sopenharmony_ci	int i;
271762306a36Sopenharmony_ci
271862306a36Sopenharmony_ci	for (i = 0; i < DPAA2_SWITCH_RX_NUM_FQS; i++) {
271962306a36Sopenharmony_ci		ethsw->fq[i].store =
272062306a36Sopenharmony_ci			dpaa2_io_store_create(DPAA2_SWITCH_STORE_SIZE,
272162306a36Sopenharmony_ci					      ethsw->dev);
272262306a36Sopenharmony_ci		if (!ethsw->fq[i].store) {
272362306a36Sopenharmony_ci			dev_err(ethsw->dev, "dpaa2_io_store_create failed\n");
272462306a36Sopenharmony_ci			while (--i >= 0)
272562306a36Sopenharmony_ci				dpaa2_io_store_destroy(ethsw->fq[i].store);
272662306a36Sopenharmony_ci			return -ENOMEM;
272762306a36Sopenharmony_ci		}
272862306a36Sopenharmony_ci	}
272962306a36Sopenharmony_ci
273062306a36Sopenharmony_ci	return 0;
273162306a36Sopenharmony_ci}
273262306a36Sopenharmony_ci
273362306a36Sopenharmony_cistatic void dpaa2_switch_destroy_rings(struct ethsw_core *ethsw)
273462306a36Sopenharmony_ci{
273562306a36Sopenharmony_ci	int i;
273662306a36Sopenharmony_ci
273762306a36Sopenharmony_ci	for (i = 0; i < DPAA2_SWITCH_RX_NUM_FQS; i++)
273862306a36Sopenharmony_ci		dpaa2_io_store_destroy(ethsw->fq[i].store);
273962306a36Sopenharmony_ci}
274062306a36Sopenharmony_ci
274162306a36Sopenharmony_cistatic int dpaa2_switch_pull_fq(struct dpaa2_switch_fq *fq)
274262306a36Sopenharmony_ci{
274362306a36Sopenharmony_ci	int err, retries = 0;
274462306a36Sopenharmony_ci
274562306a36Sopenharmony_ci	/* Try to pull from the FQ while the portal is busy and we didn't hit
274662306a36Sopenharmony_ci	 * the maximum number fo retries
274762306a36Sopenharmony_ci	 */
274862306a36Sopenharmony_ci	do {
274962306a36Sopenharmony_ci		err = dpaa2_io_service_pull_fq(NULL, fq->fqid, fq->store);
275062306a36Sopenharmony_ci		cpu_relax();
275162306a36Sopenharmony_ci	} while (err == -EBUSY && retries++ < DPAA2_SWITCH_SWP_BUSY_RETRIES);
275262306a36Sopenharmony_ci
275362306a36Sopenharmony_ci	if (unlikely(err))
275462306a36Sopenharmony_ci		dev_err(fq->ethsw->dev, "dpaa2_io_service_pull err %d", err);
275562306a36Sopenharmony_ci
275662306a36Sopenharmony_ci	return err;
275762306a36Sopenharmony_ci}
275862306a36Sopenharmony_ci
275962306a36Sopenharmony_ci/* Consume all frames pull-dequeued into the store */
276062306a36Sopenharmony_cistatic int dpaa2_switch_store_consume(struct dpaa2_switch_fq *fq)
276162306a36Sopenharmony_ci{
276262306a36Sopenharmony_ci	struct ethsw_core *ethsw = fq->ethsw;
276362306a36Sopenharmony_ci	int cleaned = 0, is_last;
276462306a36Sopenharmony_ci	struct dpaa2_dq *dq;
276562306a36Sopenharmony_ci	int retries = 0;
276662306a36Sopenharmony_ci
276762306a36Sopenharmony_ci	do {
276862306a36Sopenharmony_ci		/* Get the next available FD from the store */
276962306a36Sopenharmony_ci		dq = dpaa2_io_store_next(fq->store, &is_last);
277062306a36Sopenharmony_ci		if (unlikely(!dq)) {
277162306a36Sopenharmony_ci			if (retries++ >= DPAA2_SWITCH_SWP_BUSY_RETRIES) {
277262306a36Sopenharmony_ci				dev_err_once(ethsw->dev,
277362306a36Sopenharmony_ci					     "No valid dequeue response\n");
277462306a36Sopenharmony_ci				return -ETIMEDOUT;
277562306a36Sopenharmony_ci			}
277662306a36Sopenharmony_ci			continue;
277762306a36Sopenharmony_ci		}
277862306a36Sopenharmony_ci
277962306a36Sopenharmony_ci		if (fq->type == DPSW_QUEUE_RX)
278062306a36Sopenharmony_ci			dpaa2_switch_rx(fq, dpaa2_dq_fd(dq));
278162306a36Sopenharmony_ci		else
278262306a36Sopenharmony_ci			dpaa2_switch_tx_conf(fq, dpaa2_dq_fd(dq));
278362306a36Sopenharmony_ci		cleaned++;
278462306a36Sopenharmony_ci
278562306a36Sopenharmony_ci	} while (!is_last);
278662306a36Sopenharmony_ci
278762306a36Sopenharmony_ci	return cleaned;
278862306a36Sopenharmony_ci}
278962306a36Sopenharmony_ci
279062306a36Sopenharmony_ci/* NAPI poll routine */
279162306a36Sopenharmony_cistatic int dpaa2_switch_poll(struct napi_struct *napi, int budget)
279262306a36Sopenharmony_ci{
279362306a36Sopenharmony_ci	int err, cleaned = 0, store_cleaned, work_done;
279462306a36Sopenharmony_ci	struct dpaa2_switch_fq *fq;
279562306a36Sopenharmony_ci	int retries = 0;
279662306a36Sopenharmony_ci
279762306a36Sopenharmony_ci	fq = container_of(napi, struct dpaa2_switch_fq, napi);
279862306a36Sopenharmony_ci
279962306a36Sopenharmony_ci	do {
280062306a36Sopenharmony_ci		err = dpaa2_switch_pull_fq(fq);
280162306a36Sopenharmony_ci		if (unlikely(err))
280262306a36Sopenharmony_ci			break;
280362306a36Sopenharmony_ci
280462306a36Sopenharmony_ci		/* Refill pool if appropriate */
280562306a36Sopenharmony_ci		dpaa2_switch_refill_bp(fq->ethsw);
280662306a36Sopenharmony_ci
280762306a36Sopenharmony_ci		store_cleaned = dpaa2_switch_store_consume(fq);
280862306a36Sopenharmony_ci		cleaned += store_cleaned;
280962306a36Sopenharmony_ci
281062306a36Sopenharmony_ci		if (cleaned >= budget) {
281162306a36Sopenharmony_ci			work_done = budget;
281262306a36Sopenharmony_ci			goto out;
281362306a36Sopenharmony_ci		}
281462306a36Sopenharmony_ci
281562306a36Sopenharmony_ci	} while (store_cleaned);
281662306a36Sopenharmony_ci
281762306a36Sopenharmony_ci	/* We didn't consume the entire budget, so finish napi and re-enable
281862306a36Sopenharmony_ci	 * data availability notifications
281962306a36Sopenharmony_ci	 */
282062306a36Sopenharmony_ci	napi_complete_done(napi, cleaned);
282162306a36Sopenharmony_ci	do {
282262306a36Sopenharmony_ci		err = dpaa2_io_service_rearm(NULL, &fq->nctx);
282362306a36Sopenharmony_ci		cpu_relax();
282462306a36Sopenharmony_ci	} while (err == -EBUSY && retries++ < DPAA2_SWITCH_SWP_BUSY_RETRIES);
282562306a36Sopenharmony_ci
282662306a36Sopenharmony_ci	work_done = max(cleaned, 1);
282762306a36Sopenharmony_ciout:
282862306a36Sopenharmony_ci
282962306a36Sopenharmony_ci	return work_done;
283062306a36Sopenharmony_ci}
283162306a36Sopenharmony_ci
283262306a36Sopenharmony_cistatic void dpaa2_switch_fqdan_cb(struct dpaa2_io_notification_ctx *nctx)
283362306a36Sopenharmony_ci{
283462306a36Sopenharmony_ci	struct dpaa2_switch_fq *fq;
283562306a36Sopenharmony_ci
283662306a36Sopenharmony_ci	fq = container_of(nctx, struct dpaa2_switch_fq, nctx);
283762306a36Sopenharmony_ci
283862306a36Sopenharmony_ci	napi_schedule(&fq->napi);
283962306a36Sopenharmony_ci}
284062306a36Sopenharmony_ci
284162306a36Sopenharmony_cistatic int dpaa2_switch_setup_dpio(struct ethsw_core *ethsw)
284262306a36Sopenharmony_ci{
284362306a36Sopenharmony_ci	struct dpsw_ctrl_if_queue_cfg queue_cfg;
284462306a36Sopenharmony_ci	struct dpaa2_io_notification_ctx *nctx;
284562306a36Sopenharmony_ci	int err, i, j;
284662306a36Sopenharmony_ci
284762306a36Sopenharmony_ci	for (i = 0; i < DPAA2_SWITCH_RX_NUM_FQS; i++) {
284862306a36Sopenharmony_ci		nctx = &ethsw->fq[i].nctx;
284962306a36Sopenharmony_ci
285062306a36Sopenharmony_ci		/* Register a new software context for the FQID.
285162306a36Sopenharmony_ci		 * By using NULL as the first parameter, we specify that we do
285262306a36Sopenharmony_ci		 * not care on which cpu are interrupts received for this queue
285362306a36Sopenharmony_ci		 */
285462306a36Sopenharmony_ci		nctx->is_cdan = 0;
285562306a36Sopenharmony_ci		nctx->id = ethsw->fq[i].fqid;
285662306a36Sopenharmony_ci		nctx->desired_cpu = DPAA2_IO_ANY_CPU;
285762306a36Sopenharmony_ci		nctx->cb = dpaa2_switch_fqdan_cb;
285862306a36Sopenharmony_ci		err = dpaa2_io_service_register(NULL, nctx, ethsw->dev);
285962306a36Sopenharmony_ci		if (err) {
286062306a36Sopenharmony_ci			err = -EPROBE_DEFER;
286162306a36Sopenharmony_ci			goto err_register;
286262306a36Sopenharmony_ci		}
286362306a36Sopenharmony_ci
286462306a36Sopenharmony_ci		queue_cfg.options = DPSW_CTRL_IF_QUEUE_OPT_DEST |
286562306a36Sopenharmony_ci				    DPSW_CTRL_IF_QUEUE_OPT_USER_CTX;
286662306a36Sopenharmony_ci		queue_cfg.dest_cfg.dest_type = DPSW_CTRL_IF_DEST_DPIO;
286762306a36Sopenharmony_ci		queue_cfg.dest_cfg.dest_id = nctx->dpio_id;
286862306a36Sopenharmony_ci		queue_cfg.dest_cfg.priority = 0;
286962306a36Sopenharmony_ci		queue_cfg.user_ctx = nctx->qman64;
287062306a36Sopenharmony_ci
287162306a36Sopenharmony_ci		err = dpsw_ctrl_if_set_queue(ethsw->mc_io, 0,
287262306a36Sopenharmony_ci					     ethsw->dpsw_handle,
287362306a36Sopenharmony_ci					     ethsw->fq[i].type,
287462306a36Sopenharmony_ci					     &queue_cfg);
287562306a36Sopenharmony_ci		if (err)
287662306a36Sopenharmony_ci			goto err_set_queue;
287762306a36Sopenharmony_ci	}
287862306a36Sopenharmony_ci
287962306a36Sopenharmony_ci	return 0;
288062306a36Sopenharmony_ci
288162306a36Sopenharmony_cierr_set_queue:
288262306a36Sopenharmony_ci	dpaa2_io_service_deregister(NULL, nctx, ethsw->dev);
288362306a36Sopenharmony_cierr_register:
288462306a36Sopenharmony_ci	for (j = 0; j < i; j++)
288562306a36Sopenharmony_ci		dpaa2_io_service_deregister(NULL, &ethsw->fq[j].nctx,
288662306a36Sopenharmony_ci					    ethsw->dev);
288762306a36Sopenharmony_ci
288862306a36Sopenharmony_ci	return err;
288962306a36Sopenharmony_ci}
289062306a36Sopenharmony_ci
289162306a36Sopenharmony_cistatic void dpaa2_switch_free_dpio(struct ethsw_core *ethsw)
289262306a36Sopenharmony_ci{
289362306a36Sopenharmony_ci	int i;
289462306a36Sopenharmony_ci
289562306a36Sopenharmony_ci	for (i = 0; i < DPAA2_SWITCH_RX_NUM_FQS; i++)
289662306a36Sopenharmony_ci		dpaa2_io_service_deregister(NULL, &ethsw->fq[i].nctx,
289762306a36Sopenharmony_ci					    ethsw->dev);
289862306a36Sopenharmony_ci}
289962306a36Sopenharmony_ci
290062306a36Sopenharmony_cistatic int dpaa2_switch_ctrl_if_setup(struct ethsw_core *ethsw)
290162306a36Sopenharmony_ci{
290262306a36Sopenharmony_ci	int err;
290362306a36Sopenharmony_ci
290462306a36Sopenharmony_ci	/* setup FQs for Rx and Tx Conf */
290562306a36Sopenharmony_ci	err = dpaa2_switch_setup_fqs(ethsw);
290662306a36Sopenharmony_ci	if (err)
290762306a36Sopenharmony_ci		return err;
290862306a36Sopenharmony_ci
290962306a36Sopenharmony_ci	/* setup the buffer pool needed on the Rx path */
291062306a36Sopenharmony_ci	err = dpaa2_switch_setup_dpbp(ethsw);
291162306a36Sopenharmony_ci	if (err)
291262306a36Sopenharmony_ci		return err;
291362306a36Sopenharmony_ci
291462306a36Sopenharmony_ci	err = dpaa2_switch_alloc_rings(ethsw);
291562306a36Sopenharmony_ci	if (err)
291662306a36Sopenharmony_ci		goto err_free_dpbp;
291762306a36Sopenharmony_ci
291862306a36Sopenharmony_ci	err = dpaa2_switch_setup_dpio(ethsw);
291962306a36Sopenharmony_ci	if (err)
292062306a36Sopenharmony_ci		goto err_destroy_rings;
292162306a36Sopenharmony_ci
292262306a36Sopenharmony_ci	err = dpaa2_switch_seed_bp(ethsw);
292362306a36Sopenharmony_ci	if (err)
292462306a36Sopenharmony_ci		goto err_deregister_dpio;
292562306a36Sopenharmony_ci
292662306a36Sopenharmony_ci	err = dpsw_ctrl_if_enable(ethsw->mc_io, 0, ethsw->dpsw_handle);
292762306a36Sopenharmony_ci	if (err) {
292862306a36Sopenharmony_ci		dev_err(ethsw->dev, "dpsw_ctrl_if_enable err %d\n", err);
292962306a36Sopenharmony_ci		goto err_drain_dpbp;
293062306a36Sopenharmony_ci	}
293162306a36Sopenharmony_ci
293262306a36Sopenharmony_ci	return 0;
293362306a36Sopenharmony_ci
293462306a36Sopenharmony_cierr_drain_dpbp:
293562306a36Sopenharmony_ci	dpaa2_switch_drain_bp(ethsw);
293662306a36Sopenharmony_cierr_deregister_dpio:
293762306a36Sopenharmony_ci	dpaa2_switch_free_dpio(ethsw);
293862306a36Sopenharmony_cierr_destroy_rings:
293962306a36Sopenharmony_ci	dpaa2_switch_destroy_rings(ethsw);
294062306a36Sopenharmony_cierr_free_dpbp:
294162306a36Sopenharmony_ci	dpaa2_switch_free_dpbp(ethsw);
294262306a36Sopenharmony_ci
294362306a36Sopenharmony_ci	return err;
294462306a36Sopenharmony_ci}
294562306a36Sopenharmony_ci
294662306a36Sopenharmony_cistatic void dpaa2_switch_remove_port(struct ethsw_core *ethsw,
294762306a36Sopenharmony_ci				     u16 port_idx)
294862306a36Sopenharmony_ci{
294962306a36Sopenharmony_ci	struct ethsw_port_priv *port_priv = ethsw->ports[port_idx];
295062306a36Sopenharmony_ci
295162306a36Sopenharmony_ci	dpaa2_switch_port_disconnect_mac(port_priv);
295262306a36Sopenharmony_ci	free_netdev(port_priv->netdev);
295362306a36Sopenharmony_ci	ethsw->ports[port_idx] = NULL;
295462306a36Sopenharmony_ci}
295562306a36Sopenharmony_ci
295662306a36Sopenharmony_cistatic int dpaa2_switch_init(struct fsl_mc_device *sw_dev)
295762306a36Sopenharmony_ci{
295862306a36Sopenharmony_ci	struct device *dev = &sw_dev->dev;
295962306a36Sopenharmony_ci	struct ethsw_core *ethsw = dev_get_drvdata(dev);
296062306a36Sopenharmony_ci	struct dpsw_vlan_if_cfg vcfg = {0};
296162306a36Sopenharmony_ci	struct dpsw_tci_cfg tci_cfg = {0};
296262306a36Sopenharmony_ci	struct dpsw_stp_cfg stp_cfg;
296362306a36Sopenharmony_ci	int err;
296462306a36Sopenharmony_ci	u16 i;
296562306a36Sopenharmony_ci
296662306a36Sopenharmony_ci	ethsw->dev_id = sw_dev->obj_desc.id;
296762306a36Sopenharmony_ci
296862306a36Sopenharmony_ci	err = dpsw_open(ethsw->mc_io, 0, ethsw->dev_id, &ethsw->dpsw_handle);
296962306a36Sopenharmony_ci	if (err) {
297062306a36Sopenharmony_ci		dev_err(dev, "dpsw_open err %d\n", err);
297162306a36Sopenharmony_ci		return err;
297262306a36Sopenharmony_ci	}
297362306a36Sopenharmony_ci
297462306a36Sopenharmony_ci	err = dpsw_get_attributes(ethsw->mc_io, 0, ethsw->dpsw_handle,
297562306a36Sopenharmony_ci				  &ethsw->sw_attr);
297662306a36Sopenharmony_ci	if (err) {
297762306a36Sopenharmony_ci		dev_err(dev, "dpsw_get_attributes err %d\n", err);
297862306a36Sopenharmony_ci		goto err_close;
297962306a36Sopenharmony_ci	}
298062306a36Sopenharmony_ci
298162306a36Sopenharmony_ci	err = dpsw_get_api_version(ethsw->mc_io, 0,
298262306a36Sopenharmony_ci				   &ethsw->major,
298362306a36Sopenharmony_ci				   &ethsw->minor);
298462306a36Sopenharmony_ci	if (err) {
298562306a36Sopenharmony_ci		dev_err(dev, "dpsw_get_api_version err %d\n", err);
298662306a36Sopenharmony_ci		goto err_close;
298762306a36Sopenharmony_ci	}
298862306a36Sopenharmony_ci
298962306a36Sopenharmony_ci	/* Minimum supported DPSW version check */
299062306a36Sopenharmony_ci	if (ethsw->major < DPSW_MIN_VER_MAJOR ||
299162306a36Sopenharmony_ci	    (ethsw->major == DPSW_MIN_VER_MAJOR &&
299262306a36Sopenharmony_ci	     ethsw->minor < DPSW_MIN_VER_MINOR)) {
299362306a36Sopenharmony_ci		dev_err(dev, "DPSW version %d:%d not supported. Use firmware 10.28.0 or greater.\n",
299462306a36Sopenharmony_ci			ethsw->major, ethsw->minor);
299562306a36Sopenharmony_ci		err = -EOPNOTSUPP;
299662306a36Sopenharmony_ci		goto err_close;
299762306a36Sopenharmony_ci	}
299862306a36Sopenharmony_ci
299962306a36Sopenharmony_ci	if (!dpaa2_switch_supports_cpu_traffic(ethsw)) {
300062306a36Sopenharmony_ci		err = -EOPNOTSUPP;
300162306a36Sopenharmony_ci		goto err_close;
300262306a36Sopenharmony_ci	}
300362306a36Sopenharmony_ci
300462306a36Sopenharmony_ci	dpaa2_switch_detect_features(ethsw);
300562306a36Sopenharmony_ci
300662306a36Sopenharmony_ci	err = dpsw_reset(ethsw->mc_io, 0, ethsw->dpsw_handle);
300762306a36Sopenharmony_ci	if (err) {
300862306a36Sopenharmony_ci		dev_err(dev, "dpsw_reset err %d\n", err);
300962306a36Sopenharmony_ci		goto err_close;
301062306a36Sopenharmony_ci	}
301162306a36Sopenharmony_ci
301262306a36Sopenharmony_ci	stp_cfg.vlan_id = DEFAULT_VLAN_ID;
301362306a36Sopenharmony_ci	stp_cfg.state = DPSW_STP_STATE_FORWARDING;
301462306a36Sopenharmony_ci
301562306a36Sopenharmony_ci	for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
301662306a36Sopenharmony_ci		err = dpsw_if_disable(ethsw->mc_io, 0, ethsw->dpsw_handle, i);
301762306a36Sopenharmony_ci		if (err) {
301862306a36Sopenharmony_ci			dev_err(dev, "dpsw_if_disable err %d\n", err);
301962306a36Sopenharmony_ci			goto err_close;
302062306a36Sopenharmony_ci		}
302162306a36Sopenharmony_ci
302262306a36Sopenharmony_ci		err = dpsw_if_set_stp(ethsw->mc_io, 0, ethsw->dpsw_handle, i,
302362306a36Sopenharmony_ci				      &stp_cfg);
302462306a36Sopenharmony_ci		if (err) {
302562306a36Sopenharmony_ci			dev_err(dev, "dpsw_if_set_stp err %d for port %d\n",
302662306a36Sopenharmony_ci				err, i);
302762306a36Sopenharmony_ci			goto err_close;
302862306a36Sopenharmony_ci		}
302962306a36Sopenharmony_ci
303062306a36Sopenharmony_ci		/* Switch starts with all ports configured to VLAN 1. Need to
303162306a36Sopenharmony_ci		 * remove this setting to allow configuration at bridge join
303262306a36Sopenharmony_ci		 */
303362306a36Sopenharmony_ci		vcfg.num_ifs = 1;
303462306a36Sopenharmony_ci		vcfg.if_id[0] = i;
303562306a36Sopenharmony_ci		err = dpsw_vlan_remove_if_untagged(ethsw->mc_io, 0, ethsw->dpsw_handle,
303662306a36Sopenharmony_ci						   DEFAULT_VLAN_ID, &vcfg);
303762306a36Sopenharmony_ci		if (err) {
303862306a36Sopenharmony_ci			dev_err(dev, "dpsw_vlan_remove_if_untagged err %d\n",
303962306a36Sopenharmony_ci				err);
304062306a36Sopenharmony_ci			goto err_close;
304162306a36Sopenharmony_ci		}
304262306a36Sopenharmony_ci
304362306a36Sopenharmony_ci		tci_cfg.vlan_id = 4095;
304462306a36Sopenharmony_ci		err = dpsw_if_set_tci(ethsw->mc_io, 0, ethsw->dpsw_handle, i, &tci_cfg);
304562306a36Sopenharmony_ci		if (err) {
304662306a36Sopenharmony_ci			dev_err(dev, "dpsw_if_set_tci err %d\n", err);
304762306a36Sopenharmony_ci			goto err_close;
304862306a36Sopenharmony_ci		}
304962306a36Sopenharmony_ci
305062306a36Sopenharmony_ci		err = dpsw_vlan_remove_if(ethsw->mc_io, 0, ethsw->dpsw_handle,
305162306a36Sopenharmony_ci					  DEFAULT_VLAN_ID, &vcfg);
305262306a36Sopenharmony_ci		if (err) {
305362306a36Sopenharmony_ci			dev_err(dev, "dpsw_vlan_remove_if err %d\n", err);
305462306a36Sopenharmony_ci			goto err_close;
305562306a36Sopenharmony_ci		}
305662306a36Sopenharmony_ci	}
305762306a36Sopenharmony_ci
305862306a36Sopenharmony_ci	err = dpsw_vlan_remove(ethsw->mc_io, 0, ethsw->dpsw_handle, DEFAULT_VLAN_ID);
305962306a36Sopenharmony_ci	if (err) {
306062306a36Sopenharmony_ci		dev_err(dev, "dpsw_vlan_remove err %d\n", err);
306162306a36Sopenharmony_ci		goto err_close;
306262306a36Sopenharmony_ci	}
306362306a36Sopenharmony_ci
306462306a36Sopenharmony_ci	ethsw->workqueue = alloc_ordered_workqueue("%s_%d_ordered",
306562306a36Sopenharmony_ci						   WQ_MEM_RECLAIM, "ethsw",
306662306a36Sopenharmony_ci						   ethsw->sw_attr.id);
306762306a36Sopenharmony_ci	if (!ethsw->workqueue) {
306862306a36Sopenharmony_ci		err = -ENOMEM;
306962306a36Sopenharmony_ci		goto err_close;
307062306a36Sopenharmony_ci	}
307162306a36Sopenharmony_ci
307262306a36Sopenharmony_ci	err = dpsw_fdb_remove(ethsw->mc_io, 0, ethsw->dpsw_handle, 0);
307362306a36Sopenharmony_ci	if (err)
307462306a36Sopenharmony_ci		goto err_destroy_ordered_workqueue;
307562306a36Sopenharmony_ci
307662306a36Sopenharmony_ci	err = dpaa2_switch_ctrl_if_setup(ethsw);
307762306a36Sopenharmony_ci	if (err)
307862306a36Sopenharmony_ci		goto err_destroy_ordered_workqueue;
307962306a36Sopenharmony_ci
308062306a36Sopenharmony_ci	return 0;
308162306a36Sopenharmony_ci
308262306a36Sopenharmony_cierr_destroy_ordered_workqueue:
308362306a36Sopenharmony_ci	destroy_workqueue(ethsw->workqueue);
308462306a36Sopenharmony_ci
308562306a36Sopenharmony_cierr_close:
308662306a36Sopenharmony_ci	dpsw_close(ethsw->mc_io, 0, ethsw->dpsw_handle);
308762306a36Sopenharmony_ci	return err;
308862306a36Sopenharmony_ci}
308962306a36Sopenharmony_ci
309062306a36Sopenharmony_ci/* Add an ACL to redirect frames with specific destination MAC address to
309162306a36Sopenharmony_ci * control interface
309262306a36Sopenharmony_ci */
309362306a36Sopenharmony_cistatic int dpaa2_switch_port_trap_mac_addr(struct ethsw_port_priv *port_priv,
309462306a36Sopenharmony_ci					   const char *mac)
309562306a36Sopenharmony_ci{
309662306a36Sopenharmony_ci	struct dpaa2_switch_acl_entry acl_entry = {0};
309762306a36Sopenharmony_ci
309862306a36Sopenharmony_ci	/* Match on the destination MAC address */
309962306a36Sopenharmony_ci	ether_addr_copy(acl_entry.key.match.l2_dest_mac, mac);
310062306a36Sopenharmony_ci	eth_broadcast_addr(acl_entry.key.mask.l2_dest_mac);
310162306a36Sopenharmony_ci
310262306a36Sopenharmony_ci	/* Trap to CPU */
310362306a36Sopenharmony_ci	acl_entry.cfg.precedence = 0;
310462306a36Sopenharmony_ci	acl_entry.cfg.result.action = DPSW_ACL_ACTION_REDIRECT_TO_CTRL_IF;
310562306a36Sopenharmony_ci
310662306a36Sopenharmony_ci	return dpaa2_switch_acl_entry_add(port_priv->filter_block, &acl_entry);
310762306a36Sopenharmony_ci}
310862306a36Sopenharmony_ci
310962306a36Sopenharmony_cistatic int dpaa2_switch_port_init(struct ethsw_port_priv *port_priv, u16 port)
311062306a36Sopenharmony_ci{
311162306a36Sopenharmony_ci	const char stpa[ETH_ALEN] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x00};
311262306a36Sopenharmony_ci	struct switchdev_obj_port_vlan vlan = {
311362306a36Sopenharmony_ci		.obj.id = SWITCHDEV_OBJ_ID_PORT_VLAN,
311462306a36Sopenharmony_ci		.vid = DEFAULT_VLAN_ID,
311562306a36Sopenharmony_ci		.flags = BRIDGE_VLAN_INFO_UNTAGGED | BRIDGE_VLAN_INFO_PVID,
311662306a36Sopenharmony_ci	};
311762306a36Sopenharmony_ci	struct net_device *netdev = port_priv->netdev;
311862306a36Sopenharmony_ci	struct ethsw_core *ethsw = port_priv->ethsw_data;
311962306a36Sopenharmony_ci	struct dpaa2_switch_filter_block *filter_block;
312062306a36Sopenharmony_ci	struct dpsw_fdb_cfg fdb_cfg = {0};
312162306a36Sopenharmony_ci	struct dpsw_if_attr dpsw_if_attr;
312262306a36Sopenharmony_ci	struct dpaa2_switch_fdb *fdb;
312362306a36Sopenharmony_ci	struct dpsw_acl_cfg acl_cfg;
312462306a36Sopenharmony_ci	u16 fdb_id, acl_tbl_id;
312562306a36Sopenharmony_ci	int err;
312662306a36Sopenharmony_ci
312762306a36Sopenharmony_ci	/* Get the Tx queue for this specific port */
312862306a36Sopenharmony_ci	err = dpsw_if_get_attributes(ethsw->mc_io, 0, ethsw->dpsw_handle,
312962306a36Sopenharmony_ci				     port_priv->idx, &dpsw_if_attr);
313062306a36Sopenharmony_ci	if (err) {
313162306a36Sopenharmony_ci		netdev_err(netdev, "dpsw_if_get_attributes err %d\n", err);
313262306a36Sopenharmony_ci		return err;
313362306a36Sopenharmony_ci	}
313462306a36Sopenharmony_ci	port_priv->tx_qdid = dpsw_if_attr.qdid;
313562306a36Sopenharmony_ci
313662306a36Sopenharmony_ci	/* Create a FDB table for this particular switch port */
313762306a36Sopenharmony_ci	fdb_cfg.num_fdb_entries = ethsw->sw_attr.max_fdb_entries / ethsw->sw_attr.num_ifs;
313862306a36Sopenharmony_ci	err = dpsw_fdb_add(ethsw->mc_io, 0, ethsw->dpsw_handle,
313962306a36Sopenharmony_ci			   &fdb_id, &fdb_cfg);
314062306a36Sopenharmony_ci	if (err) {
314162306a36Sopenharmony_ci		netdev_err(netdev, "dpsw_fdb_add err %d\n", err);
314262306a36Sopenharmony_ci		return err;
314362306a36Sopenharmony_ci	}
314462306a36Sopenharmony_ci
314562306a36Sopenharmony_ci	/* Find an unused dpaa2_switch_fdb structure and use it */
314662306a36Sopenharmony_ci	fdb = dpaa2_switch_fdb_get_unused(ethsw);
314762306a36Sopenharmony_ci	fdb->fdb_id = fdb_id;
314862306a36Sopenharmony_ci	fdb->in_use = true;
314962306a36Sopenharmony_ci	fdb->bridge_dev = NULL;
315062306a36Sopenharmony_ci	port_priv->fdb = fdb;
315162306a36Sopenharmony_ci
315262306a36Sopenharmony_ci	/* We need to add VLAN 1 as the PVID on this port until it is under a
315362306a36Sopenharmony_ci	 * bridge since the DPAA2 switch is not able to handle the traffic in a
315462306a36Sopenharmony_ci	 * VLAN unaware fashion
315562306a36Sopenharmony_ci	 */
315662306a36Sopenharmony_ci	err = dpaa2_switch_port_vlans_add(netdev, &vlan);
315762306a36Sopenharmony_ci	if (err)
315862306a36Sopenharmony_ci		return err;
315962306a36Sopenharmony_ci
316062306a36Sopenharmony_ci	/* Setup the egress flooding domains (broadcast, unknown unicast */
316162306a36Sopenharmony_ci	err = dpaa2_switch_fdb_set_egress_flood(ethsw, port_priv->fdb->fdb_id);
316262306a36Sopenharmony_ci	if (err)
316362306a36Sopenharmony_ci		return err;
316462306a36Sopenharmony_ci
316562306a36Sopenharmony_ci	/* Create an ACL table to be used by this switch port */
316662306a36Sopenharmony_ci	acl_cfg.max_entries = DPAA2_ETHSW_PORT_MAX_ACL_ENTRIES;
316762306a36Sopenharmony_ci	err = dpsw_acl_add(ethsw->mc_io, 0, ethsw->dpsw_handle,
316862306a36Sopenharmony_ci			   &acl_tbl_id, &acl_cfg);
316962306a36Sopenharmony_ci	if (err) {
317062306a36Sopenharmony_ci		netdev_err(netdev, "dpsw_acl_add err %d\n", err);
317162306a36Sopenharmony_ci		return err;
317262306a36Sopenharmony_ci	}
317362306a36Sopenharmony_ci
317462306a36Sopenharmony_ci	filter_block = dpaa2_switch_filter_block_get_unused(ethsw);
317562306a36Sopenharmony_ci	filter_block->ethsw = ethsw;
317662306a36Sopenharmony_ci	filter_block->acl_id = acl_tbl_id;
317762306a36Sopenharmony_ci	filter_block->in_use = true;
317862306a36Sopenharmony_ci	filter_block->num_acl_rules = 0;
317962306a36Sopenharmony_ci	INIT_LIST_HEAD(&filter_block->acl_entries);
318062306a36Sopenharmony_ci	INIT_LIST_HEAD(&filter_block->mirror_entries);
318162306a36Sopenharmony_ci
318262306a36Sopenharmony_ci	err = dpaa2_switch_port_acl_tbl_bind(port_priv, filter_block);
318362306a36Sopenharmony_ci	if (err)
318462306a36Sopenharmony_ci		return err;
318562306a36Sopenharmony_ci
318662306a36Sopenharmony_ci	err = dpaa2_switch_port_trap_mac_addr(port_priv, stpa);
318762306a36Sopenharmony_ci	if (err)
318862306a36Sopenharmony_ci		return err;
318962306a36Sopenharmony_ci
319062306a36Sopenharmony_ci	return err;
319162306a36Sopenharmony_ci}
319262306a36Sopenharmony_ci
319362306a36Sopenharmony_cistatic void dpaa2_switch_ctrl_if_teardown(struct ethsw_core *ethsw)
319462306a36Sopenharmony_ci{
319562306a36Sopenharmony_ci	dpsw_ctrl_if_disable(ethsw->mc_io, 0, ethsw->dpsw_handle);
319662306a36Sopenharmony_ci	dpaa2_switch_free_dpio(ethsw);
319762306a36Sopenharmony_ci	dpaa2_switch_destroy_rings(ethsw);
319862306a36Sopenharmony_ci	dpaa2_switch_drain_bp(ethsw);
319962306a36Sopenharmony_ci	dpaa2_switch_free_dpbp(ethsw);
320062306a36Sopenharmony_ci}
320162306a36Sopenharmony_ci
320262306a36Sopenharmony_cistatic void dpaa2_switch_teardown(struct fsl_mc_device *sw_dev)
320362306a36Sopenharmony_ci{
320462306a36Sopenharmony_ci	struct device *dev = &sw_dev->dev;
320562306a36Sopenharmony_ci	struct ethsw_core *ethsw = dev_get_drvdata(dev);
320662306a36Sopenharmony_ci	int err;
320762306a36Sopenharmony_ci
320862306a36Sopenharmony_ci	dpaa2_switch_ctrl_if_teardown(ethsw);
320962306a36Sopenharmony_ci
321062306a36Sopenharmony_ci	destroy_workqueue(ethsw->workqueue);
321162306a36Sopenharmony_ci
321262306a36Sopenharmony_ci	err = dpsw_close(ethsw->mc_io, 0, ethsw->dpsw_handle);
321362306a36Sopenharmony_ci	if (err)
321462306a36Sopenharmony_ci		dev_warn(dev, "dpsw_close err %d\n", err);
321562306a36Sopenharmony_ci}
321662306a36Sopenharmony_ci
321762306a36Sopenharmony_cistatic void dpaa2_switch_remove(struct fsl_mc_device *sw_dev)
321862306a36Sopenharmony_ci{
321962306a36Sopenharmony_ci	struct ethsw_port_priv *port_priv;
322062306a36Sopenharmony_ci	struct ethsw_core *ethsw;
322162306a36Sopenharmony_ci	struct device *dev;
322262306a36Sopenharmony_ci	int i;
322362306a36Sopenharmony_ci
322462306a36Sopenharmony_ci	dev = &sw_dev->dev;
322562306a36Sopenharmony_ci	ethsw = dev_get_drvdata(dev);
322662306a36Sopenharmony_ci
322762306a36Sopenharmony_ci	dpaa2_switch_teardown_irqs(sw_dev);
322862306a36Sopenharmony_ci
322962306a36Sopenharmony_ci	dpsw_disable(ethsw->mc_io, 0, ethsw->dpsw_handle);
323062306a36Sopenharmony_ci
323162306a36Sopenharmony_ci	for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
323262306a36Sopenharmony_ci		port_priv = ethsw->ports[i];
323362306a36Sopenharmony_ci		unregister_netdev(port_priv->netdev);
323462306a36Sopenharmony_ci		dpaa2_switch_remove_port(ethsw, i);
323562306a36Sopenharmony_ci	}
323662306a36Sopenharmony_ci
323762306a36Sopenharmony_ci	kfree(ethsw->fdbs);
323862306a36Sopenharmony_ci	kfree(ethsw->filter_blocks);
323962306a36Sopenharmony_ci	kfree(ethsw->ports);
324062306a36Sopenharmony_ci
324162306a36Sopenharmony_ci	dpaa2_switch_teardown(sw_dev);
324262306a36Sopenharmony_ci
324362306a36Sopenharmony_ci	fsl_mc_portal_free(ethsw->mc_io);
324462306a36Sopenharmony_ci
324562306a36Sopenharmony_ci	kfree(ethsw);
324662306a36Sopenharmony_ci
324762306a36Sopenharmony_ci	dev_set_drvdata(dev, NULL);
324862306a36Sopenharmony_ci}
324962306a36Sopenharmony_ci
325062306a36Sopenharmony_cistatic int dpaa2_switch_probe_port(struct ethsw_core *ethsw,
325162306a36Sopenharmony_ci				   u16 port_idx)
325262306a36Sopenharmony_ci{
325362306a36Sopenharmony_ci	struct ethsw_port_priv *port_priv;
325462306a36Sopenharmony_ci	struct device *dev = ethsw->dev;
325562306a36Sopenharmony_ci	struct net_device *port_netdev;
325662306a36Sopenharmony_ci	int err;
325762306a36Sopenharmony_ci
325862306a36Sopenharmony_ci	port_netdev = alloc_etherdev(sizeof(struct ethsw_port_priv));
325962306a36Sopenharmony_ci	if (!port_netdev) {
326062306a36Sopenharmony_ci		dev_err(dev, "alloc_etherdev error\n");
326162306a36Sopenharmony_ci		return -ENOMEM;
326262306a36Sopenharmony_ci	}
326362306a36Sopenharmony_ci
326462306a36Sopenharmony_ci	port_priv = netdev_priv(port_netdev);
326562306a36Sopenharmony_ci	port_priv->netdev = port_netdev;
326662306a36Sopenharmony_ci	port_priv->ethsw_data = ethsw;
326762306a36Sopenharmony_ci
326862306a36Sopenharmony_ci	mutex_init(&port_priv->mac_lock);
326962306a36Sopenharmony_ci
327062306a36Sopenharmony_ci	port_priv->idx = port_idx;
327162306a36Sopenharmony_ci	port_priv->stp_state = BR_STATE_FORWARDING;
327262306a36Sopenharmony_ci
327362306a36Sopenharmony_ci	SET_NETDEV_DEV(port_netdev, dev);
327462306a36Sopenharmony_ci	port_netdev->netdev_ops = &dpaa2_switch_port_ops;
327562306a36Sopenharmony_ci	port_netdev->ethtool_ops = &dpaa2_switch_port_ethtool_ops;
327662306a36Sopenharmony_ci
327762306a36Sopenharmony_ci	port_netdev->needed_headroom = DPAA2_SWITCH_NEEDED_HEADROOM;
327862306a36Sopenharmony_ci
327962306a36Sopenharmony_ci	port_priv->bcast_flood = true;
328062306a36Sopenharmony_ci	port_priv->ucast_flood = true;
328162306a36Sopenharmony_ci
328262306a36Sopenharmony_ci	/* Set MTU limits */
328362306a36Sopenharmony_ci	port_netdev->min_mtu = ETH_MIN_MTU;
328462306a36Sopenharmony_ci	port_netdev->max_mtu = ETHSW_MAX_FRAME_LENGTH;
328562306a36Sopenharmony_ci
328662306a36Sopenharmony_ci	/* Populate the private port structure so that later calls to
328762306a36Sopenharmony_ci	 * dpaa2_switch_port_init() can use it.
328862306a36Sopenharmony_ci	 */
328962306a36Sopenharmony_ci	ethsw->ports[port_idx] = port_priv;
329062306a36Sopenharmony_ci
329162306a36Sopenharmony_ci	/* The DPAA2 switch's ingress path depends on the VLAN table,
329262306a36Sopenharmony_ci	 * thus we are not able to disable VLAN filtering.
329362306a36Sopenharmony_ci	 */
329462306a36Sopenharmony_ci	port_netdev->features = NETIF_F_HW_VLAN_CTAG_FILTER |
329562306a36Sopenharmony_ci				NETIF_F_HW_VLAN_STAG_FILTER |
329662306a36Sopenharmony_ci				NETIF_F_HW_TC;
329762306a36Sopenharmony_ci
329862306a36Sopenharmony_ci	err = dpaa2_switch_port_init(port_priv, port_idx);
329962306a36Sopenharmony_ci	if (err)
330062306a36Sopenharmony_ci		goto err_port_probe;
330162306a36Sopenharmony_ci
330262306a36Sopenharmony_ci	err = dpaa2_switch_port_set_mac_addr(port_priv);
330362306a36Sopenharmony_ci	if (err)
330462306a36Sopenharmony_ci		goto err_port_probe;
330562306a36Sopenharmony_ci
330662306a36Sopenharmony_ci	err = dpaa2_switch_port_set_learning(port_priv, false);
330762306a36Sopenharmony_ci	if (err)
330862306a36Sopenharmony_ci		goto err_port_probe;
330962306a36Sopenharmony_ci	port_priv->learn_ena = false;
331062306a36Sopenharmony_ci
331162306a36Sopenharmony_ci	err = dpaa2_switch_port_connect_mac(port_priv);
331262306a36Sopenharmony_ci	if (err)
331362306a36Sopenharmony_ci		goto err_port_probe;
331462306a36Sopenharmony_ci
331562306a36Sopenharmony_ci	return 0;
331662306a36Sopenharmony_ci
331762306a36Sopenharmony_cierr_port_probe:
331862306a36Sopenharmony_ci	free_netdev(port_netdev);
331962306a36Sopenharmony_ci	ethsw->ports[port_idx] = NULL;
332062306a36Sopenharmony_ci
332162306a36Sopenharmony_ci	return err;
332262306a36Sopenharmony_ci}
332362306a36Sopenharmony_ci
332462306a36Sopenharmony_cistatic int dpaa2_switch_probe(struct fsl_mc_device *sw_dev)
332562306a36Sopenharmony_ci{
332662306a36Sopenharmony_ci	struct device *dev = &sw_dev->dev;
332762306a36Sopenharmony_ci	struct ethsw_core *ethsw;
332862306a36Sopenharmony_ci	int i, err;
332962306a36Sopenharmony_ci
333062306a36Sopenharmony_ci	/* Allocate switch core*/
333162306a36Sopenharmony_ci	ethsw = kzalloc(sizeof(*ethsw), GFP_KERNEL);
333262306a36Sopenharmony_ci
333362306a36Sopenharmony_ci	if (!ethsw)
333462306a36Sopenharmony_ci		return -ENOMEM;
333562306a36Sopenharmony_ci
333662306a36Sopenharmony_ci	ethsw->dev = dev;
333762306a36Sopenharmony_ci	ethsw->iommu_domain = iommu_get_domain_for_dev(dev);
333862306a36Sopenharmony_ci	dev_set_drvdata(dev, ethsw);
333962306a36Sopenharmony_ci
334062306a36Sopenharmony_ci	err = fsl_mc_portal_allocate(sw_dev, FSL_MC_IO_ATOMIC_CONTEXT_PORTAL,
334162306a36Sopenharmony_ci				     &ethsw->mc_io);
334262306a36Sopenharmony_ci	if (err) {
334362306a36Sopenharmony_ci		if (err == -ENXIO)
334462306a36Sopenharmony_ci			err = -EPROBE_DEFER;
334562306a36Sopenharmony_ci		else
334662306a36Sopenharmony_ci			dev_err(dev, "fsl_mc_portal_allocate err %d\n", err);
334762306a36Sopenharmony_ci		goto err_free_drvdata;
334862306a36Sopenharmony_ci	}
334962306a36Sopenharmony_ci
335062306a36Sopenharmony_ci	err = dpaa2_switch_init(sw_dev);
335162306a36Sopenharmony_ci	if (err)
335262306a36Sopenharmony_ci		goto err_free_cmdport;
335362306a36Sopenharmony_ci
335462306a36Sopenharmony_ci	ethsw->ports = kcalloc(ethsw->sw_attr.num_ifs, sizeof(*ethsw->ports),
335562306a36Sopenharmony_ci			       GFP_KERNEL);
335662306a36Sopenharmony_ci	if (!(ethsw->ports)) {
335762306a36Sopenharmony_ci		err = -ENOMEM;
335862306a36Sopenharmony_ci		goto err_teardown;
335962306a36Sopenharmony_ci	}
336062306a36Sopenharmony_ci
336162306a36Sopenharmony_ci	ethsw->fdbs = kcalloc(ethsw->sw_attr.num_ifs, sizeof(*ethsw->fdbs),
336262306a36Sopenharmony_ci			      GFP_KERNEL);
336362306a36Sopenharmony_ci	if (!ethsw->fdbs) {
336462306a36Sopenharmony_ci		err = -ENOMEM;
336562306a36Sopenharmony_ci		goto err_free_ports;
336662306a36Sopenharmony_ci	}
336762306a36Sopenharmony_ci
336862306a36Sopenharmony_ci	ethsw->filter_blocks = kcalloc(ethsw->sw_attr.num_ifs,
336962306a36Sopenharmony_ci				       sizeof(*ethsw->filter_blocks),
337062306a36Sopenharmony_ci				       GFP_KERNEL);
337162306a36Sopenharmony_ci	if (!ethsw->filter_blocks) {
337262306a36Sopenharmony_ci		err = -ENOMEM;
337362306a36Sopenharmony_ci		goto err_free_fdbs;
337462306a36Sopenharmony_ci	}
337562306a36Sopenharmony_ci
337662306a36Sopenharmony_ci	for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
337762306a36Sopenharmony_ci		err = dpaa2_switch_probe_port(ethsw, i);
337862306a36Sopenharmony_ci		if (err)
337962306a36Sopenharmony_ci			goto err_free_netdev;
338062306a36Sopenharmony_ci	}
338162306a36Sopenharmony_ci
338262306a36Sopenharmony_ci	/* Add a NAPI instance for each of the Rx queues. The first port's
338362306a36Sopenharmony_ci	 * net_device will be associated with the instances since we do not have
338462306a36Sopenharmony_ci	 * different queues for each switch ports.
338562306a36Sopenharmony_ci	 */
338662306a36Sopenharmony_ci	for (i = 0; i < DPAA2_SWITCH_RX_NUM_FQS; i++)
338762306a36Sopenharmony_ci		netif_napi_add(ethsw->ports[0]->netdev, &ethsw->fq[i].napi,
338862306a36Sopenharmony_ci			       dpaa2_switch_poll);
338962306a36Sopenharmony_ci
339062306a36Sopenharmony_ci	/* Setup IRQs */
339162306a36Sopenharmony_ci	err = dpaa2_switch_setup_irqs(sw_dev);
339262306a36Sopenharmony_ci	if (err)
339362306a36Sopenharmony_ci		goto err_stop;
339462306a36Sopenharmony_ci
339562306a36Sopenharmony_ci	/* By convention, if the mirror port is equal to the number of switch
339662306a36Sopenharmony_ci	 * interfaces, then mirroring of any kind is disabled.
339762306a36Sopenharmony_ci	 */
339862306a36Sopenharmony_ci	ethsw->mirror_port =  ethsw->sw_attr.num_ifs;
339962306a36Sopenharmony_ci
340062306a36Sopenharmony_ci	/* Register the netdev only when the entire setup is done and the
340162306a36Sopenharmony_ci	 * switch port interfaces are ready to receive traffic
340262306a36Sopenharmony_ci	 */
340362306a36Sopenharmony_ci	for (i = 0; i < ethsw->sw_attr.num_ifs; i++) {
340462306a36Sopenharmony_ci		err = register_netdev(ethsw->ports[i]->netdev);
340562306a36Sopenharmony_ci		if (err < 0) {
340662306a36Sopenharmony_ci			dev_err(dev, "register_netdev error %d\n", err);
340762306a36Sopenharmony_ci			goto err_unregister_ports;
340862306a36Sopenharmony_ci		}
340962306a36Sopenharmony_ci	}
341062306a36Sopenharmony_ci
341162306a36Sopenharmony_ci	return 0;
341262306a36Sopenharmony_ci
341362306a36Sopenharmony_cierr_unregister_ports:
341462306a36Sopenharmony_ci	for (i--; i >= 0; i--)
341562306a36Sopenharmony_ci		unregister_netdev(ethsw->ports[i]->netdev);
341662306a36Sopenharmony_ci	dpaa2_switch_teardown_irqs(sw_dev);
341762306a36Sopenharmony_cierr_stop:
341862306a36Sopenharmony_ci	dpsw_disable(ethsw->mc_io, 0, ethsw->dpsw_handle);
341962306a36Sopenharmony_cierr_free_netdev:
342062306a36Sopenharmony_ci	for (i--; i >= 0; i--)
342162306a36Sopenharmony_ci		dpaa2_switch_remove_port(ethsw, i);
342262306a36Sopenharmony_ci	kfree(ethsw->filter_blocks);
342362306a36Sopenharmony_cierr_free_fdbs:
342462306a36Sopenharmony_ci	kfree(ethsw->fdbs);
342562306a36Sopenharmony_cierr_free_ports:
342662306a36Sopenharmony_ci	kfree(ethsw->ports);
342762306a36Sopenharmony_ci
342862306a36Sopenharmony_cierr_teardown:
342962306a36Sopenharmony_ci	dpaa2_switch_teardown(sw_dev);
343062306a36Sopenharmony_ci
343162306a36Sopenharmony_cierr_free_cmdport:
343262306a36Sopenharmony_ci	fsl_mc_portal_free(ethsw->mc_io);
343362306a36Sopenharmony_ci
343462306a36Sopenharmony_cierr_free_drvdata:
343562306a36Sopenharmony_ci	kfree(ethsw);
343662306a36Sopenharmony_ci	dev_set_drvdata(dev, NULL);
343762306a36Sopenharmony_ci
343862306a36Sopenharmony_ci	return err;
343962306a36Sopenharmony_ci}
344062306a36Sopenharmony_ci
344162306a36Sopenharmony_cistatic const struct fsl_mc_device_id dpaa2_switch_match_id_table[] = {
344262306a36Sopenharmony_ci	{
344362306a36Sopenharmony_ci		.vendor = FSL_MC_VENDOR_FREESCALE,
344462306a36Sopenharmony_ci		.obj_type = "dpsw",
344562306a36Sopenharmony_ci	},
344662306a36Sopenharmony_ci	{ .vendor = 0x0 }
344762306a36Sopenharmony_ci};
344862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(fslmc, dpaa2_switch_match_id_table);
344962306a36Sopenharmony_ci
345062306a36Sopenharmony_cistatic struct fsl_mc_driver dpaa2_switch_drv = {
345162306a36Sopenharmony_ci	.driver = {
345262306a36Sopenharmony_ci		.name = KBUILD_MODNAME,
345362306a36Sopenharmony_ci	},
345462306a36Sopenharmony_ci	.probe = dpaa2_switch_probe,
345562306a36Sopenharmony_ci	.remove = dpaa2_switch_remove,
345662306a36Sopenharmony_ci	.match_id_table = dpaa2_switch_match_id_table
345762306a36Sopenharmony_ci};
345862306a36Sopenharmony_ci
345962306a36Sopenharmony_cistatic struct notifier_block dpaa2_switch_port_nb __read_mostly = {
346062306a36Sopenharmony_ci	.notifier_call = dpaa2_switch_port_netdevice_event,
346162306a36Sopenharmony_ci};
346262306a36Sopenharmony_ci
346362306a36Sopenharmony_cistatic struct notifier_block dpaa2_switch_port_switchdev_nb = {
346462306a36Sopenharmony_ci	.notifier_call = dpaa2_switch_port_event,
346562306a36Sopenharmony_ci};
346662306a36Sopenharmony_ci
346762306a36Sopenharmony_cistatic struct notifier_block dpaa2_switch_port_switchdev_blocking_nb = {
346862306a36Sopenharmony_ci	.notifier_call = dpaa2_switch_port_blocking_event,
346962306a36Sopenharmony_ci};
347062306a36Sopenharmony_ci
347162306a36Sopenharmony_cistatic int dpaa2_switch_register_notifiers(void)
347262306a36Sopenharmony_ci{
347362306a36Sopenharmony_ci	int err;
347462306a36Sopenharmony_ci
347562306a36Sopenharmony_ci	err = register_netdevice_notifier(&dpaa2_switch_port_nb);
347662306a36Sopenharmony_ci	if (err) {
347762306a36Sopenharmony_ci		pr_err("dpaa2-switch: failed to register net_device notifier (%d)\n", err);
347862306a36Sopenharmony_ci		return err;
347962306a36Sopenharmony_ci	}
348062306a36Sopenharmony_ci
348162306a36Sopenharmony_ci	err = register_switchdev_notifier(&dpaa2_switch_port_switchdev_nb);
348262306a36Sopenharmony_ci	if (err) {
348362306a36Sopenharmony_ci		pr_err("dpaa2-switch: failed to register switchdev notifier (%d)\n", err);
348462306a36Sopenharmony_ci		goto err_switchdev_nb;
348562306a36Sopenharmony_ci	}
348662306a36Sopenharmony_ci
348762306a36Sopenharmony_ci	err = register_switchdev_blocking_notifier(&dpaa2_switch_port_switchdev_blocking_nb);
348862306a36Sopenharmony_ci	if (err) {
348962306a36Sopenharmony_ci		pr_err("dpaa2-switch: failed to register switchdev blocking notifier (%d)\n", err);
349062306a36Sopenharmony_ci		goto err_switchdev_blocking_nb;
349162306a36Sopenharmony_ci	}
349262306a36Sopenharmony_ci
349362306a36Sopenharmony_ci	return 0;
349462306a36Sopenharmony_ci
349562306a36Sopenharmony_cierr_switchdev_blocking_nb:
349662306a36Sopenharmony_ci	unregister_switchdev_notifier(&dpaa2_switch_port_switchdev_nb);
349762306a36Sopenharmony_cierr_switchdev_nb:
349862306a36Sopenharmony_ci	unregister_netdevice_notifier(&dpaa2_switch_port_nb);
349962306a36Sopenharmony_ci
350062306a36Sopenharmony_ci	return err;
350162306a36Sopenharmony_ci}
350262306a36Sopenharmony_ci
350362306a36Sopenharmony_cistatic void dpaa2_switch_unregister_notifiers(void)
350462306a36Sopenharmony_ci{
350562306a36Sopenharmony_ci	int err;
350662306a36Sopenharmony_ci
350762306a36Sopenharmony_ci	err = unregister_switchdev_blocking_notifier(&dpaa2_switch_port_switchdev_blocking_nb);
350862306a36Sopenharmony_ci	if (err)
350962306a36Sopenharmony_ci		pr_err("dpaa2-switch: failed to unregister switchdev blocking notifier (%d)\n",
351062306a36Sopenharmony_ci		       err);
351162306a36Sopenharmony_ci
351262306a36Sopenharmony_ci	err = unregister_switchdev_notifier(&dpaa2_switch_port_switchdev_nb);
351362306a36Sopenharmony_ci	if (err)
351462306a36Sopenharmony_ci		pr_err("dpaa2-switch: failed to unregister switchdev notifier (%d)\n", err);
351562306a36Sopenharmony_ci
351662306a36Sopenharmony_ci	err = unregister_netdevice_notifier(&dpaa2_switch_port_nb);
351762306a36Sopenharmony_ci	if (err)
351862306a36Sopenharmony_ci		pr_err("dpaa2-switch: failed to unregister net_device notifier (%d)\n", err);
351962306a36Sopenharmony_ci}
352062306a36Sopenharmony_ci
352162306a36Sopenharmony_cistatic int __init dpaa2_switch_driver_init(void)
352262306a36Sopenharmony_ci{
352362306a36Sopenharmony_ci	int err;
352462306a36Sopenharmony_ci
352562306a36Sopenharmony_ci	err = fsl_mc_driver_register(&dpaa2_switch_drv);
352662306a36Sopenharmony_ci	if (err)
352762306a36Sopenharmony_ci		return err;
352862306a36Sopenharmony_ci
352962306a36Sopenharmony_ci	err = dpaa2_switch_register_notifiers();
353062306a36Sopenharmony_ci	if (err) {
353162306a36Sopenharmony_ci		fsl_mc_driver_unregister(&dpaa2_switch_drv);
353262306a36Sopenharmony_ci		return err;
353362306a36Sopenharmony_ci	}
353462306a36Sopenharmony_ci
353562306a36Sopenharmony_ci	return 0;
353662306a36Sopenharmony_ci}
353762306a36Sopenharmony_ci
353862306a36Sopenharmony_cistatic void __exit dpaa2_switch_driver_exit(void)
353962306a36Sopenharmony_ci{
354062306a36Sopenharmony_ci	dpaa2_switch_unregister_notifiers();
354162306a36Sopenharmony_ci	fsl_mc_driver_unregister(&dpaa2_switch_drv);
354262306a36Sopenharmony_ci}
354362306a36Sopenharmony_ci
354462306a36Sopenharmony_cimodule_init(dpaa2_switch_driver_init);
354562306a36Sopenharmony_cimodule_exit(dpaa2_switch_driver_exit);
354662306a36Sopenharmony_ci
354762306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
354862306a36Sopenharmony_ciMODULE_DESCRIPTION("DPAA2 Ethernet Switch Driver");
3549