Contributors: 1
Author Tokens Token Proportion Commits Commit Proportion
Wei Fang 10764 100.00% 11 100.00%
Total 10764 11


// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
/*
 * NXP NETC switch driver
 * Copyright 2025-2026 NXP
 */

#include <linux/clk.h>
#include <linux/etherdevice.h>
#include <linux/fsl/enetc_mdio.h>
#include <linux/if_bridge.h>
#include <linux/if_vlan.h>
#include <linux/of_mdio.h>

#include "netc_switch.h"

static struct netc_fdb_entry *
netc_lookup_fdb_entry(struct netc_switch *priv,
		      const unsigned char *addr,
		      u16 vid)
{
	struct netc_fdb_entry *entry;

	hlist_for_each_entry(entry, &priv->fdb_list, node)
		if (ether_addr_equal(entry->keye.mac_addr, addr) &&
		    le16_to_cpu(entry->keye.fid) == vid)
			return entry;

	return NULL;
}

static void netc_destroy_fdb_list(struct netc_switch *priv)
{
	struct netc_fdb_entry *entry;
	struct hlist_node *tmp;

	hlist_for_each_entry_safe(entry, tmp, &priv->fdb_list, node)
		netc_del_fdb_entry(entry);
}

static struct netc_vlan_entry *
netc_lookup_vlan_entry(struct netc_switch *priv, u16 vid)
{
	struct netc_vlan_entry *entry;

	hlist_for_each_entry(entry, &priv->vlan_list, node)
		if (entry->vid == vid)
			return entry;

	return NULL;
}

static void netc_destroy_vlan_list(struct netc_switch *priv)
{
	struct netc_vlan_entry *entry;
	struct hlist_node *tmp;

	hlist_for_each_entry_safe(entry, tmp, &priv->vlan_list, node)
		netc_del_vlan_entry(entry);
}

static enum dsa_tag_protocol
netc_get_tag_protocol(struct dsa_switch *ds, int port,
		      enum dsa_tag_protocol mprot)
{
	return DSA_TAG_PROTO_NETC;
}

static void netc_port_rmw(struct netc_port *np, u32 reg,
			  u32 mask, u32 val)
{
	u32 old, new;

	WARN_ON((mask | val) != mask);

	old = netc_port_rd(np, reg);
	new = (old & ~mask) | val;
	if (new == old)
		return;

	netc_port_wr(np, reg, new);
}

static void netc_mac_port_wr(struct netc_port *np, u32 reg, u32 val)
{
	if (is_netc_pseudo_port(np))
		return;

	netc_port_wr(np, reg, val);
	if (np->caps.pmac)
		netc_port_wr(np, reg + NETC_PMAC_OFFSET, val);
}

/* netc_mac_port_rmw() is used to synchronize the configurations of eMAC
 * and pMAC to maintain consistency. This function should not be used if
 * differentiated settings are required.
 */
static void netc_mac_port_rmw(struct netc_port *np, u32 reg,
			      u32 mask, u32 val)
{
	u32 old, new;

	if (is_netc_pseudo_port(np))
		return;

	WARN_ON((mask | val) != mask);

	old = netc_port_rd(np, reg);
	new = (old & ~mask) | val;
	if (new == old)
		return;

	netc_port_wr(np, reg, new);
	if (np->caps.pmac)
		netc_port_wr(np, reg + NETC_PMAC_OFFSET, new);
}

static void netc_port_get_capability(struct netc_port *np)
{
	u32 val;

	val = netc_port_rd(np, NETC_PMCAPR);
	if (val & PMCAPR_HD)
		np->caps.half_duplex = true;

	if (FIELD_GET(PMCAPR_FP, val) == FP_SUPPORT)
		np->caps.pmac = true;

	val = netc_port_rd(np, NETC_PCAPR);
	if (val & PCAPR_LINK_TYPE)
		np->caps.pseudo_link = true;
}

static int netc_port_get_info_from_dt(struct netc_port *np,
				      struct device_node *node,
				      struct device *dev)
{
	if (of_find_property(node, "clock-names", NULL)) {
		np->ref_clk = devm_get_clk_from_child(dev, node, "ref");
		if (IS_ERR(np->ref_clk)) {
			dev_err(dev, "Port %d cannot get reference clock\n",
				np->dp->index);
			return PTR_ERR(np->ref_clk);
		}
	}

	return 0;
}

static int netc_port_create_emdio_bus(struct netc_port *np,
				      struct device_node *node)
{
	struct netc_switch *priv = np->switch_priv;
	struct enetc_mdio_priv *mdio_priv;
	struct device *dev = priv->dev;
	struct enetc_hw *hw;
	struct mii_bus *bus;
	int err;

	hw = enetc_hw_alloc(dev, np->iobase);
	if (IS_ERR(hw))
		return dev_err_probe(dev, PTR_ERR(hw),
				     "Failed to allocate enetc_hw\n");

	bus = devm_mdiobus_alloc_size(dev, sizeof(*mdio_priv));
	if (!bus)
		return -ENOMEM;

	bus->name = "NXP NETC switch external MDIO Bus";
	bus->read = enetc_mdio_read_c22;
	bus->write = enetc_mdio_write_c22;
	bus->read_c45 = enetc_mdio_read_c45;
	bus->write_c45 = enetc_mdio_write_c45;
	bus->parent = dev;
	mdio_priv = bus->priv;
	mdio_priv->hw = hw;
	mdio_priv->mdio_base = NETC_EMDIO_BASE;
	snprintf(bus->id, MII_BUS_ID_SIZE, "%s-p%d-emdio",
		 dev_name(dev), np->dp->index);

	err = devm_of_mdiobus_register(dev, bus, node);
	if (err)
		return dev_err_probe(dev, err,
				     "Cannot register EMDIO bus\n");

	np->emdio = bus;

	return 0;
}

static int netc_port_create_mdio_bus(struct netc_port *np,
				     struct device_node *node)
{
	struct device_node *mdio_node;
	int err;

	mdio_node = of_get_child_by_name(node, "mdio");
	if (mdio_node) {
		err = netc_port_create_emdio_bus(np, mdio_node);
		of_node_put(mdio_node);
		if (err)
			return err;
	}

	return 0;
}

static int netc_init_switch_id(struct netc_switch *priv)
{
	struct netc_switch_regs *regs = &priv->regs;
	struct dsa_switch *ds = priv->ds;

	/* The value of 0 is reserved for the VEPA switch and cannot
	 * be used. So 'dsa,member' is a required property for NETC
	 * switch, the member is used to specify the switch ID, which
	 * cannot be zero. This way, the hardware switch ID and the
	 * software switch ID are consistent.
	 */
	if (ds->index > FIELD_MAX(SWCR_SWID) || !ds->index) {
		dev_err(priv->dev, "Switch index %d out of range\n",
			ds->index);
		return -ERANGE;
	}

	netc_base_wr(regs, NETC_SWCR, ds->index);

	return 0;
}

static void netc_get_switch_capabilities(struct netc_switch *priv)
{
	struct netc_switch_regs *regs = &priv->regs;
	u32 val;

	val = netc_base_rd(regs, NETC_HTMCAPR);
	priv->htmcapr_num_words = FIELD_GET(HTMCAPR_NUM_WORDS, val);

	val = netc_base_rd(regs, NETC_BPCAPR);
	priv->num_bp = FIELD_GET(BPCAPR_NUM_BP, val);
}

static int netc_init_all_ports(struct netc_switch *priv)
{
	struct device *dev = priv->dev;
	struct netc_port *np;
	struct dsa_port *dp;
	int ett_offset = 0;
	int err;

	priv->ports = devm_kcalloc(dev, priv->info->num_ports,
				   sizeof(struct netc_port *),
				   GFP_KERNEL);
	if (!priv->ports)
		return -ENOMEM;

	/* Some DSA interfaces may set the port even it is disabled, such
	 * as .port_disable(), .port_stp_state_set() and so on. To avoid
	 * crash caused by accessing NULL port pointer, each port is
	 * allocated its own memory. Otherwise, we need to check whether
	 * the port pointer is NULL in these interfaces. The latter is
	 * difficult for us to cover.
	 */
	for (int i = 0; i < priv->info->num_ports; i++) {
		np = devm_kzalloc(dev, sizeof(*np), GFP_KERNEL);
		if (!np)
			return -ENOMEM;

		np->switch_priv = priv;
		np->iobase = priv->regs.port + PORT_IOBASE(i);
		netc_port_get_capability(np);
		priv->ports[i] = np;
	}

	dsa_switch_for_each_available_port(dp, priv->ds) {
		np = priv->ports[dp->index];
		np->dp = dp;
		np->ett_offset = ett_offset++;
		priv->port_bitmap |= BIT(dp->index);

		err = netc_port_get_info_from_dt(np, dp->dn, dev);
		if (err)
			return err;

		if (dsa_port_is_user(dp)) {
			err = netc_port_create_mdio_bus(np, dp->dn);
			if (err) {
				dev_err(dev, "Failed to create MDIO bus\n");
				return err;
			}
		}
	}

	return 0;
}

static void netc_init_ntmp_tbl_versions(struct netc_switch *priv)
{
	struct ntmp_user *ntmp = &priv->ntmp;

	/* All tables default to version 0 */
	memset(&ntmp->tbl, 0, sizeof(ntmp->tbl));
}

static int netc_init_all_cbdrs(struct netc_switch *priv)
{
	struct netc_switch_regs *regs = &priv->regs;
	struct ntmp_user *ntmp = &priv->ntmp;
	int i, err;

	ntmp->cbdr_num = NETC_CBDR_NUM;
	ntmp->dev = priv->dev;
	ntmp->ring = devm_kcalloc(ntmp->dev, ntmp->cbdr_num,
				  sizeof(struct netc_cbdr),
				  GFP_KERNEL);
	if (!ntmp->ring)
		return -ENOMEM;

	for (i = 0; i < ntmp->cbdr_num; i++) {
		struct netc_cbdr *cbdr = &ntmp->ring[i];
		struct netc_cbdr_regs cbdr_regs;

		cbdr_regs.pir = regs->base + NETC_CBDRPIR(i);
		cbdr_regs.cir = regs->base + NETC_CBDRCIR(i);
		cbdr_regs.mr = regs->base + NETC_CBDRMR(i);
		cbdr_regs.bar0 = regs->base + NETC_CBDRBAR0(i);
		cbdr_regs.bar1 = regs->base + NETC_CBDRBAR1(i);
		cbdr_regs.lenr = regs->base + NETC_CBDRLENR(i);

		err = ntmp_init_cbdr(cbdr, ntmp->dev, &cbdr_regs);
		if (err)
			goto free_cbdrs;
	}

	return 0;

free_cbdrs:
	for (i--; i >= 0; i--)
		ntmp_free_cbdr(&ntmp->ring[i]);

	return err;
}

static void netc_remove_all_cbdrs(struct netc_switch *priv)
{
	struct ntmp_user *ntmp = &priv->ntmp;

	for (int i = 0; i < NETC_CBDR_NUM; i++)
		ntmp_free_cbdr(&ntmp->ring[i]);
}

static u32 netc_num_available_ports(struct netc_switch *priv)
{
	struct dsa_port *dp;
	u32 num_ports = 0;

	dsa_switch_for_each_available_port(dp, priv->ds)
		num_ports++;

	return num_ports;
}

static int netc_init_ntmp_bitmap_sizes(struct netc_switch *priv)
{
	u32 num_ports = netc_num_available_ports(priv);
	struct netc_switch_regs *regs = &priv->regs;
	struct ntmp_user *ntmp = &priv->ntmp;
	u32 val;

	if (!num_ports)
		return -EINVAL;

	val = netc_base_rd(regs, NETC_ETTCAPR);
	ntmp->ett_bitmap_size = NETC_GET_NUM_ENTRIES(val) / num_ports;
	if (!ntmp->ett_bitmap_size)
		return -EINVAL;

	val = netc_base_rd(regs, NETC_ECTCAPR);
	ntmp->ect_bitmap_size = NETC_GET_NUM_ENTRIES(val) / num_ports;
	if (!ntmp->ect_bitmap_size)
		return -EINVAL;

	return 0;
}

static int netc_init_ntmp_bitmaps(struct netc_switch *priv)
{
	struct ntmp_user *ntmp = &priv->ntmp;

	ntmp->ett_gid_bitmap = bitmap_zalloc(ntmp->ett_bitmap_size,
					     GFP_KERNEL);
	if (!ntmp->ett_gid_bitmap)
		return -ENOMEM;

	ntmp->ect_gid_bitmap = bitmap_zalloc(ntmp->ect_bitmap_size,
					     GFP_KERNEL);
	if (!ntmp->ect_gid_bitmap)
		goto free_ett_gid_bitmap;

	return 0;

free_ett_gid_bitmap:
	bitmap_free(ntmp->ett_gid_bitmap);
	ntmp->ett_gid_bitmap = NULL;

	return -ENOMEM;
}

static void netc_free_ntmp_bitmaps(struct netc_switch *priv)
{
	struct ntmp_user *ntmp = &priv->ntmp;

	bitmap_free(ntmp->ect_gid_bitmap);
	ntmp->ect_gid_bitmap = NULL;

	bitmap_free(ntmp->ett_gid_bitmap);
	ntmp->ett_gid_bitmap = NULL;
}

static int netc_init_ntmp_user(struct netc_switch *priv)
{
	int err;

	netc_init_ntmp_tbl_versions(priv);

	err = netc_init_ntmp_bitmap_sizes(priv);
	if (err)
		return err;

	err = netc_init_ntmp_bitmaps(priv);
	if (err)
		return err;

	err = netc_init_all_cbdrs(priv);
	if (err)
		goto free_ntmp_bitmaps;

	return 0;

free_ntmp_bitmaps:
	netc_free_ntmp_bitmaps(priv);

	return err;
}

static void netc_free_ntmp_user(struct netc_switch *priv)
{
	netc_remove_all_cbdrs(priv);
	netc_free_ntmp_bitmaps(priv);
}

static void netc_clean_fdbt_ageing_entries(struct work_struct *work)
{
	struct delayed_work *dwork = to_delayed_work(work);
	struct netc_switch *priv;

	priv = container_of(dwork, struct netc_switch, fdbt_ageing_work);

	/* Update the activity element in FDB table */
	mutex_lock(&priv->fdbt_lock);
	ntmp_fdbt_update_activity_element(&priv->ntmp);
	/* Delete the ageing entries after the activity element is updated */
	ntmp_fdbt_delete_ageing_entries(&priv->ntmp, NETC_FDBT_AGEING_THRESH);
	mutex_unlock(&priv->fdbt_lock);

	if (atomic_read(&priv->br_cnt))
		schedule_delayed_work(&priv->fdbt_ageing_work,
				      READ_ONCE(priv->fdbt_ageing_delay));
}

static void netc_switch_dos_default_config(struct netc_switch *priv)
{
	struct netc_switch_regs *regs = &priv->regs;
	u32 val;

	val = DOSL2CR_SAMEADDR | DOSL2CR_MSAMCC;
	netc_base_wr(regs, NETC_DOSL2CR, val);

	val = DOSL3CR_SAMEADDR | DOSL3CR_IPSAMCC;
	netc_base_wr(regs, NETC_DOSL3CR, val);
}

static void netc_switch_vfht_default_config(struct netc_switch *priv)
{
	struct netc_switch_regs *regs = &priv->regs;
	u32 val;

	val = netc_base_rd(regs, NETC_VFHTDECR2);

	/* If no match is found in the VLAN Filter table, then VFHTDECR2[MLO]
	 * will take effect. VFHTDECR2[MLO] is set to "Software MAC learning
	 * secure" by default. Notice BPCR[MLO] will override VFHTDECR2[MLO]
	 * if its value is not zero.
	 */
	val = u32_replace_bits(val, MLO_SW_SEC, VFHTDECR2_MLO);
	val = u32_replace_bits(val, MFO_NO_MATCH_DISCARD, VFHTDECR2_MFO);
	netc_base_wr(regs, NETC_VFHTDECR2, val);
}

static void netc_port_set_max_frame_size(struct netc_port *np,
					 u32 max_frame_size)
{
	netc_mac_port_wr(np, NETC_PM_MAXFRM(0),
			 max_frame_size & PM_MAXFRAM);
}

static void netc_switch_fixed_config(struct netc_switch *priv)
{
	netc_switch_dos_default_config(priv);
	netc_switch_vfht_default_config(priv);
}

static void netc_port_set_tc_max_sdu(struct netc_port *np,
				     int tc, u32 max_sdu)
{
	u32 val = FIELD_PREP(PTCTMSDUR_MAXSDU, max_sdu) |
		  FIELD_PREP(PTCTMSDUR_SDU_TYPE, SDU_TYPE_MPDU);

	netc_port_wr(np, NETC_PTCTMSDUR(tc), val);
}

static void netc_port_set_all_tc_msdu(struct netc_port *np)
{
	for (int tc = 0; tc < NETC_TC_NUM; tc++)
		netc_port_set_tc_max_sdu(np, tc, NETC_MAX_FRAME_LEN);
}

static void netc_port_set_mlo(struct netc_port *np, enum netc_mlo mlo)
{
	netc_port_rmw(np, NETC_BPCR, BPCR_MLO, FIELD_PREP(BPCR_MLO, mlo));
}

static void netc_port_set_pvid(struct netc_port *np, u16 pvid)
{
	netc_port_rmw(np, NETC_BPDVR, BPDVR_VID, pvid);
}

static void netc_port_set_vlan_aware(struct netc_port *np, bool aware)
{
	netc_port_rmw(np, NETC_BPDVR, BPDVR_RXVAM,
		      aware ? 0 : BPDVR_RXVAM);
}

static void netc_port_fixed_config(struct netc_port *np)
{
	/* Default IPV and DR setting */
	netc_port_rmw(np, NETC_PQOSMR, PQOSMR_VS | PQOSMR_VE,
		      PQOSMR_VS | PQOSMR_VE);

	/* Enable L2 and L3 DOS */
	netc_port_rmw(np, NETC_PCR, PCR_L2DOSE | PCR_L3DOSE,
		      PCR_L2DOSE | PCR_L3DOSE);

	/* Set the quanta value of TX PAUSE frame */
	netc_mac_port_wr(np, NETC_PM_PAUSE_QUANTA(0), NETC_PAUSE_QUANTA);

	/* When a quanta timer counts down and reaches this value,
	 * the MAC sends a refresh PAUSE frame with the programmed
	 * full quanta value if a pause condition still exists.
	 */
	netc_mac_port_wr(np, NETC_PM_PAUSE_THRESH(0), NETC_PAUSE_THRESH);
}

static void netc_port_default_config(struct netc_port *np)
{
	netc_port_fixed_config(np);

	/* Default VLAN unaware */
	netc_port_set_vlan_aware(np, false);

	if (dsa_port_is_cpu(np->dp))
		/* For CPU port, source port pruning is disabled */
		netc_port_rmw(np, NETC_BPCR, BPCR_SRCPRND, BPCR_SRCPRND);
	else
		netc_port_set_mlo(np, MLO_DISABLE);

	netc_port_set_max_frame_size(np, NETC_MAX_FRAME_LEN);
	netc_port_set_all_tc_msdu(np);
}

static u32 netc_available_port_bitmap(struct netc_switch *priv)
{
	struct dsa_port *dp;
	u32 bitmap = 0;

	dsa_switch_for_each_available_port(dp, priv->ds)
		bitmap |= BIT(dp->index);

	return bitmap;
}

static int netc_add_standalone_vlan_entry(struct netc_switch *priv)
{
	u32 bitmap_stg = VFT_STG_ID(0) | netc_available_port_bitmap(priv);
	struct vft_cfge_data *cfge;
	u16 cfg;
	int err;

	cfge = kzalloc_obj(*cfge);
	if (!cfge)
		return -ENOMEM;

	cfge->bitmap_stg = cpu_to_le32(bitmap_stg);
	cfge->et_eid = cpu_to_le32(NTMP_NULL_ENTRY_ID);
	cfge->fid = cpu_to_le16(NETC_STANDALONE_PVID);

	/* For standalone ports, MAC learning needs to be disabled, so frames
	 * from other user ports will not be forwarded to the standalone ports,
	 * because there are no FDB entries on the standalone ports. Also, the
	 * frames received by the standalone ports cannot be flooded to other
	 * ports, so MAC forwarding option needs to be set to
	 * MFO_NO_MATCH_DISCARD, so the frames will be discarded rather than
	 * flooding to other ports.
	 */
	cfg = FIELD_PREP(VFT_MLO, MLO_DISABLE) |
	      FIELD_PREP(VFT_MFO, MFO_NO_MATCH_DISCARD);
	cfge->cfg = cpu_to_le16(cfg);

	err = ntmp_vft_add_entry(&priv->ntmp, NETC_STANDALONE_PVID, cfge);
	if (err)
		dev_err(priv->dev,
			"Failed to add standalone VLAN entry\n");

	kfree(cfge);

	return err;
}

static int netc_port_add_fdb_entry(struct netc_port *np,
				   const unsigned char *addr, u16 vid)
{
	struct netc_switch *priv = np->switch_priv;
	struct netc_fdb_entry *entry;
	struct fdbt_keye_data *keye;
	struct fdbt_cfge_data *cfge;
	int port = np->dp->index;
	u32 cfg = 0;
	int err;

	entry = kzalloc_obj(*entry);
	if (!entry)
		return -ENOMEM;

	keye = &entry->keye;
	cfge = &entry->cfge;
	ether_addr_copy(keye->mac_addr, addr);
	keye->fid = cpu_to_le16(vid);

	cfge->port_bitmap = cpu_to_le32(BIT(port));
	cfge->cfg = cpu_to_le32(cfg);
	cfge->et_eid = cpu_to_le32(NTMP_NULL_ENTRY_ID);

	err = ntmp_fdbt_add_entry(&priv->ntmp, &entry->entry_id, keye, cfge);
	if (err) {
		kfree(entry);

		return err;
	}

	netc_add_fdb_entry(priv, entry);

	return 0;
}

static int netc_port_set_fdb_entry(struct netc_port *np,
				   const unsigned char *addr, u16 vid)
{
	struct netc_switch *priv = np->switch_priv;
	struct netc_fdb_entry *entry;
	struct fdbt_cfge_data *cfge;
	int port = np->dp->index;
	__le32 old_port_bitmap;
	int err = 0;

	mutex_lock(&priv->fdbt_lock);

	entry = netc_lookup_fdb_entry(priv, addr, vid);
	if (!entry) {
		err = netc_port_add_fdb_entry(np, addr, vid);
		if (err)
			dev_err(priv->dev,
				"Failed to add FDB entry on port %d\n",
				port);

		goto unlock_fdbt;
	}

	cfge = &entry->cfge;
	/* If the entry already exists on the port, return 0 directly */
	if (unlikely(cfge->port_bitmap & cpu_to_le32(BIT(port))))
		goto unlock_fdbt;

	/* If the entry already exists, but not on this port, we need to
	 * update the port bitmap. In general, it should only be valid
	 * for multicast or broadcast address.
	 */
	old_port_bitmap = cfge->port_bitmap;
	if (is_multicast_ether_addr(addr))
		cfge->port_bitmap |= cpu_to_le32(BIT(port));
	else
		cfge->port_bitmap = cpu_to_le32(BIT(port));

	err = ntmp_fdbt_update_entry(&priv->ntmp, entry->entry_id, cfge);
	if (err) {
		cfge->port_bitmap = old_port_bitmap;
		dev_err(priv->dev, "Failed to set FDB entry on port %d\n",
			port);
	}

unlock_fdbt:
	mutex_unlock(&priv->fdbt_lock);

	return err;
}

static int netc_port_del_fdb_entry(struct netc_port *np,
				   const unsigned char *addr, u16 vid)
{
	struct netc_switch *priv = np->switch_priv;
	struct ntmp_user *ntmp = &priv->ntmp;
	struct netc_fdb_entry *entry;
	struct fdbt_cfge_data *cfge;
	int port = np->dp->index;
	int err = 0;

	mutex_lock(&priv->fdbt_lock);

	entry = netc_lookup_fdb_entry(priv, addr, vid);
	if (unlikely(!entry))
		/* The hardware-learned dynamic FDB entries cannot be deleted
		 * through .port_fdb_del() interface.
		 * For NTF_MASTER path: Since hardware-learned dynamic FDB
		 * entries are never synchronized back to the bridge software
		 * database. br_fdb_delete() -> br_fdb_find() cannot find the
		 * FDB entry, so .port_fdb_del() will not be called.
		 * For NTF_SELF path: dsa_user_netdev_ops does not implement
		 * ndo_fdb_del(), so rtnl_fdb_del() falls back to
		 * ndo_dflt_fdb_del(), which only supports NUD_PERMANENT static
		 * entries and rejects all others with -EINVAL.
		 */
		goto unlock_fdbt;

	cfge = &entry->cfge;
	if (unlikely(!(cfge->port_bitmap & cpu_to_le32(BIT(port)))))
		goto unlock_fdbt;

	if (cfge->port_bitmap != cpu_to_le32(BIT(port))) {
		/* If the entry also exists on other ports, we need to
		 * update the entry in the FDB table.
		 */
		cfge->port_bitmap &= cpu_to_le32(~BIT(port));
		err = ntmp_fdbt_update_entry(ntmp, entry->entry_id, cfge);
		if (err) {
			cfge->port_bitmap |= cpu_to_le32(BIT(port));
			goto unlock_fdbt;
		}
	} else {
		/* If the entry only exists on this port, just delete
		 * it from the FDB table.
		 */
		err = ntmp_fdbt_delete_entry(ntmp, entry->entry_id);
		if (err)
			goto unlock_fdbt;

		netc_del_fdb_entry(entry);
	}

unlock_fdbt:
	mutex_unlock(&priv->fdbt_lock);

	return err;
}

static int netc_add_standalone_fdb_bcast_entry(struct netc_switch *priv)
{
	const u8 bcast[ETH_ALEN] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
	struct dsa_port *dp, *cpu_dp = NULL;

	dsa_switch_for_each_cpu_port(dp, priv->ds) {
		/* The switch has only one CPU port, so only need to find
		 * the first CPU port to break out of the loop.
		 */
		cpu_dp = dp;
		break;
	}

	if (!cpu_dp)
		return -ENODEV;

	/* If the user port acts as a standalone port, then its PVID is 0,
	 * MLO is set to "disable MAC learning" and MFO is set to "discard
	 * frames if no matching entry found in FDB table". Therefore, we
	 * need to add a broadcast FDB entry on the CPU port so that the
	 * broadcast frames received on the user port can be forwarded to
	 * the CPU port.
	 */
	return netc_port_set_fdb_entry(NETC_PORT(priv->ds, cpu_dp->index),
				       bcast, NETC_STANDALONE_PVID);
}

static void netc_port_set_pbpmcr(struct netc_port *np, u64 mapping)
{
	u32 pbpmcr0 = lower_32_bits(mapping);
	u32 pbpmcr1 = upper_32_bits(mapping);

	netc_port_wr(np, NETC_PBPMCR0, pbpmcr0);
	netc_port_wr(np, NETC_PBPMCR1, pbpmcr1);
}

static void netc_ipv_to_buffer_pool_mapping(struct netc_switch *priv)
{
	int bp_per_port = priv->num_bp / priv->info->num_ports;
	int q = NETC_IPV_NUM / bp_per_port;
	int r = NETC_IPV_NUM % bp_per_port;
	int num = q + r;

	/* IPV-to-buffer-pool mapping per port:
	 * Each port is allocated 'bp_per_port' buffer pools and supports 8
	 * IPVs, where a higher IPV indicates a higher frame priority. Each
	 * IPV can be mapped to only one buffer pool, from hardware design
	 * perspective, bp_per_port will not be greater than 8. So 'q' will
	 * not be 0.
	 *
	 * The mapping rule is as follows:
	 * - The first 'num' IPVs share the port's first buffer pool (index
	 * 'base_id').
	 * - After that, every 'q' IPVs share one buffer pool, with pool
	 * indices increasing sequentially.
	 */
	for (int i = 0; i < priv->info->num_ports; i++) {
		u32 base_id = i * bp_per_port;
		u32 bp_id = base_id;
		u64 mapping = 0;

		for (int ipv = 0; ipv < NETC_IPV_NUM; ipv++) {
			/* Update the buffer pool index */
			if (ipv >= num)
				bp_id = base_id + ((ipv - num) / q) + 1;

			mapping |= (u64)bp_id << (ipv * 8);
		}

		netc_port_set_pbpmcr(priv->ports[i], mapping);
	}
}

static int netc_switch_bpt_default_config(struct netc_switch *priv)
{
	if (priv->num_bp < priv->info->num_ports)
		return -EINVAL;

	priv->bpt_list = devm_kcalloc(priv->dev, priv->num_bp,
				      sizeof(struct bpt_cfge_data),
				      GFP_KERNEL);
	if (!priv->bpt_list)
		return -ENOMEM;

	/* Initialize the maximum threshold of each buffer pool entry */
	for (int i = 0; i < priv->num_bp; i++) {
		struct bpt_cfge_data *cfge = &priv->bpt_list[i];
		int err;

		cfge->max_thresh = cpu_to_le16(NETC_BP_THRESH);
		err = ntmp_bpt_update_entry(&priv->ntmp, i, cfge);
		if (err)
			return err;
	}

	netc_ipv_to_buffer_pool_mapping(priv);

	return 0;
}

static int netc_setup(struct dsa_switch *ds)
{
	struct netc_switch *priv = ds->priv;
	struct dsa_port *dp;
	int err;

	err = netc_init_switch_id(priv);
	if (err)
		return err;

	netc_get_switch_capabilities(priv);

	err = netc_init_all_ports(priv);
	if (err)
		return err;

	err = netc_init_ntmp_user(priv);
	if (err)
		return err;

	INIT_HLIST_HEAD(&priv->fdb_list);
	mutex_init(&priv->fdbt_lock);
	priv->fdbt_ageing_delay = NETC_FDBT_AGEING_DELAY;
	atomic_set(&priv->br_cnt, 0);
	INIT_DELAYED_WORK(&priv->fdbt_ageing_work,
			  netc_clean_fdbt_ageing_entries);
	INIT_HLIST_HEAD(&priv->vlan_list);
	mutex_init(&priv->vft_lock);

	netc_switch_fixed_config(priv);

	/* default setting for ports */
	dsa_switch_for_each_available_port(dp, ds)
		netc_port_default_config(priv->ports[dp->index]);

	err = netc_switch_bpt_default_config(priv);
	if (err)
		goto free_lock_and_ntmp_user;

	err = netc_add_standalone_vlan_entry(priv);
	if (err)
		goto free_lock_and_ntmp_user;

	err = netc_add_standalone_fdb_bcast_entry(priv);
	if (err)
		goto free_lock_and_ntmp_user;

	return 0;

free_lock_and_ntmp_user:
	/* No need to clear the hardware state, netc_setup() is only called
	 * when the driver is bound, and FLR will be performed to reset the
	 * hardware state.
	 */
	mutex_destroy(&priv->fdbt_lock);
	mutex_destroy(&priv->vft_lock);
	netc_free_ntmp_user(priv);

	return err;
}

static void netc_destroy_all_lists(struct netc_switch *priv)
{
	netc_destroy_fdb_list(priv);
	mutex_destroy(&priv->fdbt_lock);
	netc_destroy_vlan_list(priv);
	mutex_destroy(&priv->vft_lock);
}

static void netc_free_host_flood_rules(struct netc_switch *priv)
{
	struct dsa_port *dp;

	dsa_switch_for_each_user_port(dp, priv->ds) {
		struct netc_port *np = priv->ports[dp->index];

		/* No need to clear the hardware IPFT entry. Because PCIe
		 * FLR will be performed when the switch is re-registered,
		 * it will reset hardware state. So only need to free the
		 * memory to avoid memory leak.
		 */
		kfree(np->host_flood);
		np->host_flood = NULL;
	}
}

static void netc_teardown(struct dsa_switch *ds)
{
	struct netc_switch *priv = ds->priv;

	disable_delayed_work_sync(&priv->fdbt_ageing_work);
	netc_destroy_all_lists(priv);
	netc_free_host_flood_rules(priv);
	netc_free_ntmp_user(priv);
}

static bool netc_port_is_emdio_consumer(struct device_node *node)
{
	struct device_node *mdio_node;

	/* If the port node has phy-handle property and it does
	 * not contain a mdio child node, then the port is the
	 * EMDIO consumer.
	 */
	mdio_node = of_get_child_by_name(node, "mdio");
	if (!mdio_node)
		return true;

	of_node_put(mdio_node);

	return false;
}

/* Currently, phylink_of_phy_connect() is called by dsa_user_create(),
 * so if the switch uses the external MDIO controller (like the EMDIO
 * function) to manage the external PHYs. The MDIO bus may not be
 * created when phylink_of_phy_connect() is called, so it will return
 * an error and cause the switch driver to fail to probe.
 * This workaround can be removed when DSA phylink_of_phy_connect()
 * calls are moved from probe() to ndo_open().
 */
static int netc_switch_check_emdio_is_ready(struct device *dev)
{
	struct device_node *ports, *phy_node;
	struct phy_device *phydev;
	int err = 0;

	ports = of_get_child_by_name(dev->of_node, "ethernet-ports");
	if (!ports) {
		dev_err(dev, "Cannot find the ethernet-ports node\n");
		return -EINVAL;
	}

	for_each_available_child_of_node_scoped(ports, child) {
		/* If the node does not have phy-handle property, then the
		 * port does not connect to a PHY, so the port is not the
		 * EMDIO consumer.
		 */
		phy_node = of_parse_phandle(child, "phy-handle", 0);
		if (!phy_node)
			continue;

		/* Note that from the hardware perspective, the switch ports
		 * do not support sharing the MDIO bus defined under one port.
		 * Each port can only access its own external PHY through its
		 * port MDIO bus.
		 */
		if (!netc_port_is_emdio_consumer(child)) {
			of_node_put(phy_node);
			continue;
		}

		phydev = of_phy_find_device(phy_node);
		of_node_put(phy_node);
		if (!phydev) {
			err = -EPROBE_DEFER;
			goto out;
		}

		put_device(&phydev->mdio.dev);
	}

out:
	of_node_put(ports);

	return err;
}

static int netc_switch_pci_init(struct pci_dev *pdev)
{
	struct device *dev = &pdev->dev;
	struct netc_switch_regs *regs;
	struct netc_switch *priv;
	void __iomem *base;
	int err;

	pcie_flr(pdev);
	err = pcim_enable_device(pdev);
	if (err)
		return dev_err_probe(dev, err, "Failed to enable device\n");

	err = pcim_request_all_regions(pdev, KBUILD_MODNAME);
	if (err)
		return dev_err_probe(dev, err, "Failed to request regions\n");

	/* The command BD rings and NTMP tables need DMA. No need to check
	 * the return value, because it never returns fail when the mask is
	 * DMA_BIT_MASK(64), see dma-api-howto.rst.
	 */
	dma_set_mask_and_coherent(dev, DMA_BIT_MASK(64));

	if (pci_resource_len(pdev, NETC_REGS_BAR) < NETC_REGS_SIZE) {
		return dev_err_probe(dev, -EINVAL,
				     "Invalid register space size\n");
	}

	base = pcim_iomap(pdev, NETC_REGS_BAR, 0);
	if (!base)
		return dev_err_probe(dev, -ENXIO, "pcim_iomap() failed\n");

	pci_set_master(pdev);

	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
	if (!priv)
		return -ENOMEM;

	priv->pdev = pdev;
	priv->dev = dev;

	regs = &priv->regs;
	regs->base = base;
	regs->port = regs->base + NETC_REGS_PORT_BASE;
	regs->global = regs->base + NETC_REGS_GLOBAL_BASE;
	pci_set_drvdata(pdev, priv);

	return 0;
}

static void netc_switch_get_ip_revision(struct netc_switch *priv)
{
	struct netc_switch_regs *regs = &priv->regs;
	u32 val = netc_glb_rd(regs, NETC_IPBRR0);

	priv->revision = FIELD_GET(IPBRR0_IP_REV, val);
}

static void netc_init_ett_cfge(struct ett_cfge_data *cfge,
			       bool untagged, u32 ect_eid)
{
	u32 vuda_sqta = FMTEID_VUDA_SQTA;
	u16 efm_cfg = 0;

	if (ect_eid != NTMP_NULL_ENTRY_ID) {
		/* Increase egress frame counter */
		efm_cfg |= FIELD_PREP(ETT_ECA, ETT_ECA_INC);
		cfge->ec_eid = cpu_to_le32(ect_eid);
	}

	/* If egress rule is VLAN untagged */
	if (untagged) {
		/* delete outer VLAN tag */
		vuda_sqta |= FIELD_PREP(FMTEID_VUDA, FMTEID_VUDA_DEL_OTAG);
		/* length change: twos-complement notation */
		efm_cfg |= FIELD_PREP(ETT_EFM_LEN_CHANGE,
				      ETT_FRM_LEN_DEL_VLAN);
	}

	cfge->efm_eid = cpu_to_le32(vuda_sqta);
	cfge->efm_cfg = cpu_to_le16(efm_cfg);
}

static int netc_add_ett_entry(struct netc_switch *priv, bool untagged,
			      u32 ett_eid, u32 ect_eid)
{
	struct ntmp_user *ntmp = &priv->ntmp;
	struct ett_cfge_data cfge = {};

	netc_init_ett_cfge(&cfge, untagged, ect_eid);

	return ntmp_ett_add_entry(ntmp, ett_eid, &cfge);
}

static int netc_update_ett_entry(struct netc_switch *priv, bool untagged,
				 u32 ett_eid, u32 ect_eid)
{
	struct ntmp_user *ntmp = &priv->ntmp;
	struct ett_cfge_data cfge = {};

	netc_init_ett_cfge(&cfge, untagged, ect_eid);

	return ntmp_ett_update_entry(ntmp, ett_eid, &cfge);
}

static int netc_add_ett_group_entries(struct netc_switch *priv,
				      u32 untagged_port_bitmap,
				      u32 ett_base_eid,
				      u32 ect_base_eid)
{
	struct netc_port **ports = priv->ports;
	u32 ett_eid, ect_eid;
	bool untagged;
	int i, err;

	for (i = 0; i < priv->info->num_ports; i++) {
		if (!ports[i]->dp)
			continue;

		untagged = !!(untagged_port_bitmap & BIT(i));
		ett_eid = ett_base_eid + ports[i]->ett_offset;
		ect_eid = NTMP_NULL_ENTRY_ID;
		if (ect_base_eid != NTMP_NULL_ENTRY_ID)
			ect_eid = ect_base_eid + ports[i]->ett_offset;

		err = netc_add_ett_entry(priv, untagged, ett_eid, ect_eid);
		if (err)
			goto clear_ett_entries;
	}

	return 0;

clear_ett_entries:
	while (--i >= 0) {
		if (!ports[i]->dp)
			continue;

		ett_eid = ett_base_eid + ports[i]->ett_offset;
		ntmp_ett_delete_entry(&priv->ntmp, ett_eid);
	}

	return err;
}

static int netc_add_vlan_egress_rule(struct netc_switch *priv,
				     struct netc_vlan_entry *entry)
{
	u32 num_ports = netc_num_available_ports(priv);
	struct ntmp_user *ntmp = &priv->ntmp;
	u32 ect_eid = NTMP_NULL_ENTRY_ID;
	u32 ett_eid, ett_gid, ect_gid;
	int err;

	/* Step 1: Find available egress counter table entries and update
	 * these entries.
	 */
	ect_gid = ntmp_lookup_free_eid(ntmp->ect_gid_bitmap,
				       ntmp->ect_bitmap_size);
	if (ect_gid == NTMP_NULL_ENTRY_ID) {
		dev_info(priv->dev,
			 "No egress counter table entries available\n");
	} else {
		ect_eid = ect_gid * num_ports;
		for (int i = 0; i < num_ports; i++)
			/* There is no need to check the return value, the only
			 * issue is that the entry's counter might be inaccurate,
			 * but it will not affect the functionality, it is only
			 * for future debugging.
			 */
			ntmp_ect_update_entry(ntmp, ect_eid + i);
	}

	/* Step 2: Find available egress treatment table entries and add
	 * these entries.
	 */
	ett_gid = ntmp_lookup_free_eid(ntmp->ett_gid_bitmap,
				       ntmp->ett_bitmap_size);
	if (ett_gid == NTMP_NULL_ENTRY_ID) {
		dev_err(priv->dev,
			"No egress treatment table entries available\n");
		err = -ENOSPC;
		goto clear_ect_gid;
	}

	ett_eid = ett_gid * num_ports;
	err = netc_add_ett_group_entries(priv, entry->untagged_port_bitmap,
					 ett_eid, ect_eid);
	if (err)
		goto clear_ett_gid;

	entry->cfge.et_eid = cpu_to_le32(ett_eid);
	entry->ect_gid = ect_gid;

	return 0;

clear_ett_gid:
	ntmp_clear_eid_bitmap(ntmp->ett_gid_bitmap, ett_gid);

clear_ect_gid:
	if (ect_gid != NTMP_NULL_ENTRY_ID)
		ntmp_clear_eid_bitmap(ntmp->ect_gid_bitmap, ect_gid);

	return err;
}

static void netc_delete_vlan_egress_rule(struct netc_switch *priv,
					 struct netc_vlan_entry *entry)
{
	u32 num_ports = netc_num_available_ports(priv);
	struct ntmp_user *ntmp = &priv->ntmp;
	u32 ett_eid, ett_gid;

	ett_eid = le32_to_cpu(entry->cfge.et_eid);
	if (ett_eid == NTMP_NULL_ENTRY_ID)
		return;

	ett_gid = ett_eid / num_ports;
	ntmp_clear_eid_bitmap(ntmp->ett_gid_bitmap, ett_gid);
	for (int i = 0; i < num_ports; i++)
		ntmp_ett_delete_entry(ntmp, ett_eid + i);

	if (entry->ect_gid == NTMP_NULL_ENTRY_ID)
		return;

	ntmp_clear_eid_bitmap(ntmp->ect_gid_bitmap, entry->ect_gid);
}

static int netc_port_update_vlan_egress_rule(struct netc_port *np,
					     struct netc_vlan_entry *entry)
{
	bool untagged = !!(entry->untagged_port_bitmap & BIT(np->dp->index));
	u32 num_ports = netc_num_available_ports(np->switch_priv);
	u32 ett_eid = le32_to_cpu(entry->cfge.et_eid);
	struct netc_switch *priv = np->switch_priv;
	u32 ect_eid = NTMP_NULL_ENTRY_ID;
	int err;

	if (ett_eid == NTMP_NULL_ENTRY_ID)
		return 0;

	if (entry->ect_gid != NTMP_NULL_ENTRY_ID)
		/* Each ETT entry maps to an ECT entry if ect_gid is not NULL
		 * entry ID. The offset of the ECT entry corresponding to the
		 * port in the group is equal to ett_offset.
		 */
		ect_eid = entry->ect_gid * num_ports + np->ett_offset;

	ett_eid += np->ett_offset;
	err = netc_update_ett_entry(priv, untagged, ett_eid, ect_eid);
	if (err) {
		dev_err(priv->dev,
			"Failed to update VLAN %u egress rule on port %d\n",
			entry->vid, np->dp->index);
		return err;
	}

	if (ect_eid != NTMP_NULL_ENTRY_ID)
		ntmp_ect_update_entry(&priv->ntmp, ect_eid);

	return 0;
}

static int netc_port_add_vlan_entry(struct netc_port *np, u16 vid,
				    bool untagged)
{
	struct netc_switch *priv = np->switch_priv;
	struct netc_vlan_entry *entry;
	struct vft_cfge_data *cfge;
	u32 index = np->dp->index;
	u32 bitmap_stg;
	int err;
	u16 cfg;

	entry = kzalloc_obj(*entry);
	if (!entry)
		return -ENOMEM;

	entry->vid = vid;
	entry->ect_gid = NTMP_NULL_ENTRY_ID;

	bitmap_stg = BIT(index) | VFT_STG_ID(0);
	/* If the VID is a VLAN-unaware PVID, the CPU port needs to be
	 * a member of this VLAN.
	 */
	if (dsa_port_is_user(np->dp) &&
	    vid >= NETC_VLAN_UNAWARE_PVID(priv->ds->max_num_bridges)) {
		struct dsa_port *cpu_dp = np->dp->cpu_dp;

		bitmap_stg |= BIT(cpu_dp->index);
	}

	cfg = FIELD_PREP(VFT_MLO, MLO_HW) |
	      FIELD_PREP(VFT_MFO, MFO_NO_MATCH_FLOOD);

	cfge = &entry->cfge;
	cfge->et_eid = cpu_to_le32(NTMP_NULL_ENTRY_ID);
	cfge->bitmap_stg = cpu_to_le32(bitmap_stg);
	cfge->fid = cpu_to_le16(vid);
	cfge->cfg = cpu_to_le16(cfg);
	cfge->eta_port_bitmap = cpu_to_le32(priv->port_bitmap);

	if (untagged)
		entry->untagged_port_bitmap = BIT(index);

	err = netc_add_vlan_egress_rule(priv, entry);
	if (err)
		goto free_vlan_entry;

	err = ntmp_vft_add_entry(&priv->ntmp, vid, cfge);
	if (err) {
		dev_err(priv->dev,
			"Failed to add VLAN %u entry on port %d\n",
			vid, index);
		goto delete_vlan_egress_rule;
	}

	netc_add_vlan_entry(priv, entry);

	return 0;

delete_vlan_egress_rule:
	netc_delete_vlan_egress_rule(priv, entry);
free_vlan_entry:
	kfree(entry);

	return err;
}

static bool netc_port_vlan_egress_rule_changed(struct netc_switch *priv,
					       struct netc_vlan_entry *entry,
					       int port, bool untagged)
{
	bool old_untagged = !!(entry->untagged_port_bitmap & BIT(port));

	/* VLAN-unaware VIDs have no egress rules, so return 'false' */
	if (entry->vid >= NETC_VLAN_UNAWARE_PVID(priv->ds->max_num_bridges))
		return false;

	return old_untagged != untagged;
}

static int netc_port_set_vlan_entry(struct netc_port *np, u16 vid,
				    bool untagged)
{
	struct netc_switch *priv = np->switch_priv;
	struct netc_vlan_entry *entry;
	struct vft_cfge_data *cfge;
	int port = np->dp->index;
	bool changed;
	int err = 0;

	mutex_lock(&priv->vft_lock);

	entry = netc_lookup_vlan_entry(priv, vid);
	if (!entry) {
		err = netc_port_add_vlan_entry(np, vid, untagged);
		goto unlock_vft;
	}

	/* Check whether the egress VLAN rule is changed */
	changed = netc_port_vlan_egress_rule_changed(priv, entry, port,
						     untagged);
	if (changed) {
		entry->untagged_port_bitmap ^= BIT(port);
		err = netc_port_update_vlan_egress_rule(np, entry);
		if (err) {
			entry->untagged_port_bitmap ^= BIT(port);
			goto unlock_vft;
		}
	}

	cfge = &entry->cfge;
	if (cfge->bitmap_stg & cpu_to_le32(BIT(port)))
		goto unlock_vft;

	cfge->bitmap_stg |= cpu_to_le32(BIT(port));
	err = ntmp_vft_update_entry(&priv->ntmp, vid, cfge);
	if (err) {
		dev_err(priv->dev,
			"Failed to update VLAN %u entry on port %d\n",
			vid, port);

		goto restore_bitmap_stg;
	}

	mutex_unlock(&priv->vft_lock);

	return 0;

restore_bitmap_stg:
	cfge->bitmap_stg &= cpu_to_le32(~BIT(port));
	if (changed) {
		entry->untagged_port_bitmap ^= BIT(port);
		/* Recover the corresponding ETT entry. It doesn't matter
		 * if it fails because the bit corresponding to the port
		 * in the port bitmap of the VFT entry is not set. so the
		 * frame will not match that ETT entry.
		 */
		if (netc_port_update_vlan_egress_rule(np, entry))
			entry->untagged_port_bitmap ^= BIT(port);
	}
unlock_vft:
	mutex_unlock(&priv->vft_lock);

	return err;
}

static int netc_port_del_vlan_entry(struct netc_port *np, u16 vid)
{
	struct netc_switch *priv = np->switch_priv;
	struct netc_vlan_entry *entry;
	struct vft_cfge_data *cfge;
	int port = np->dp->index;
	u32 vlan_port_bitmap;
	int err = 0;

	mutex_lock(&priv->vft_lock);

	entry = netc_lookup_vlan_entry(priv, vid);
	if (!entry)
		goto unlock_vft;

	cfge = &entry->cfge;
	vlan_port_bitmap = FIELD_GET(VFT_PORT_MEMBERSHIP,
				     le32_to_cpu(cfge->bitmap_stg));
	/* If the VID is a VLAN-unaware PVID, we need to clear the CPU
	 * port bit of vlan_port_bitmap, so that the VLAN entry can be
	 * deleted if no user ports use this VLAN.
	 */
	if (dsa_port_is_user(np->dp) &&
	    vid >= NETC_VLAN_UNAWARE_PVID(priv->ds->max_num_bridges)) {
		struct dsa_port *cpu_dp = np->dp->cpu_dp;

		vlan_port_bitmap &= ~BIT(cpu_dp->index);
	}

	/* If the VLAN only belongs to the current port */
	if (vlan_port_bitmap == BIT(port)) {
		err = ntmp_vft_delete_entry(&priv->ntmp, vid);
		if (err)
			goto unlock_vft;

		netc_delete_vlan_egress_rule(priv, entry);
		netc_del_vlan_entry(entry);

		goto unlock_vft;
	}

	if (!(vlan_port_bitmap & BIT(port)))
		goto unlock_vft;

	cfge->bitmap_stg &= cpu_to_le32(~BIT(port));
	err = ntmp_vft_update_entry(&priv->ntmp, vid, cfge);
	if (err) {
		cfge->bitmap_stg |= cpu_to_le32(BIT(port));
		goto unlock_vft;
	}

unlock_vft:
	mutex_unlock(&priv->vft_lock);

	return err;
}

static int netc_port_enable(struct dsa_switch *ds, int port,
			    struct phy_device *phy)
{
	struct netc_port *np = NETC_PORT(ds, port);
	int err;

	if (np->enable)
		return 0;

	err = clk_prepare_enable(np->ref_clk);
	if (err) {
		dev_err(ds->dev,
			"Failed to enable enet_ref_clk of port %d\n", port);
		return err;
	}

	np->enable = true;

	return 0;
}

static void netc_port_disable(struct dsa_switch *ds, int port)
{
	struct netc_port *np = NETC_PORT(ds, port);

	/* When .port_disable() is called, .port_enable() may not have been
	 * called. In this case, both the prepare_count and enable_count of
	 * clock are 0. Calling clk_disable_unprepare() at this time will
	 * cause warnings.
	 */
	if (!np->enable)
		return;

	clk_disable_unprepare(np->ref_clk);
	np->enable = false;
}

static void netc_port_stp_state_set(struct dsa_switch *ds,
				    int port, u8 state)
{
	struct netc_port *np = NETC_PORT(ds, port);
	u32 val;

	switch (state) {
	case BR_STATE_DISABLED:
	case BR_STATE_LISTENING:
	case BR_STATE_BLOCKING:
		val = NETC_STG_STATE_DISABLED;
		break;
	case BR_STATE_LEARNING:
		val = NETC_STG_STATE_LEARNING;
		break;
	case BR_STATE_FORWARDING:
		val = NETC_STG_STATE_FORWARDING;
		break;
	default:
		return;
	}

	netc_port_wr(np, NETC_BPSTGSR, val);
}

static int netc_port_change_mtu(struct dsa_switch *ds,
				int port, int mtu)
{
	u32 max_frame_size = mtu + VLAN_ETH_HLEN + ETH_FCS_LEN;

	netc_port_set_max_frame_size(NETC_PORT(ds, port), max_frame_size);

	return 0;
}

static int netc_port_max_mtu(struct dsa_switch *ds, int port)
{
	return NETC_MAX_FRAME_LEN - VLAN_ETH_HLEN - ETH_FCS_LEN;
}

static struct net_device *netc_classify_db(struct dsa_db db)
{
	switch (db.type) {
	case DSA_DB_PORT:
		return NULL;
	case DSA_DB_BRIDGE:
		return db.bridge.dev;
	default:
		return ERR_PTR(-EOPNOTSUPP);
	}
}

static u16 netc_vlan_unaware_pvid(struct dsa_bridge *bridge)
{
	u32 br_num;

	if (!bridge)
		return NETC_STANDALONE_PVID;

	br_num = bridge->num;

	/* The br_num is supposed to be 1 ~ ds->max_num_bridges, see
	 * dsa_bridge_num_get(). Since max_num_bridges is non-zero,
	 * so dsa_port_bridge_create() will return an error if
	 * dsa_bridge_num_get() returns 0.
	 */
	if (WARN_ON(!br_num))
		return NETC_STANDALONE_PVID;

	return NETC_VLAN_UNAWARE_PVID(br_num);
}

static int netc_port_fdb_add(struct dsa_switch *ds, int port,
			     const unsigned char *addr, u16 vid,
			     struct dsa_db db)
{
	struct net_device *br_ndev = netc_classify_db(db);
	struct netc_port *np = NETC_PORT(ds, port);

	if (IS_ERR(br_ndev))
		return PTR_ERR(br_ndev);

	if (!vid)
		vid = netc_vlan_unaware_pvid(br_ndev ? &db.bridge : NULL);

	return netc_port_set_fdb_entry(np, addr, vid);
}

static int netc_port_fdb_del(struct dsa_switch *ds, int port,
			     const unsigned char *addr, u16 vid,
			     struct dsa_db db)
{
	struct net_device *br_ndev = netc_classify_db(db);
	struct netc_port *np = NETC_PORT(ds, port);

	if (IS_ERR(br_ndev))
		return PTR_ERR(br_ndev);

	if (!vid)
		vid = netc_vlan_unaware_pvid(br_ndev ? &db.bridge : NULL);

	return netc_port_del_fdb_entry(np, addr, vid);
}

static int netc_port_fdb_dump(struct dsa_switch *ds, int port,
			      dsa_fdb_dump_cb_t *cb, void *data)
{
	struct netc_switch *priv = ds->priv;
	u32 resume_eid = NTMP_NULL_ENTRY_ID;
	struct fdbt_entry_data *entry;
	struct fdbt_keye_data *keye;
	struct fdbt_cfge_data *cfge;
	u32 cfg, cnt = 0;
	bool is_static;
	int err;
	u16 vid;

	entry = kmalloc_obj(*entry);
	if (!entry)
		return -ENOMEM;

	keye = &entry->keye;
	cfge = &entry->cfge;
	mutex_lock(&priv->fdbt_lock);

	do {
		memset(entry, 0, sizeof(*entry));
		err = ntmp_fdbt_search_port_entry(&priv->ntmp, port,
						  &resume_eid, entry);
		if (err || entry->entry_id == NTMP_NULL_ENTRY_ID)
			break;

		cfg = le32_to_cpu(cfge->cfg);
		is_static = (cfg & FDBT_DYNAMIC) ? false : true;
		vid = le16_to_cpu(keye->fid);
		if (vid >= NETC_VLAN_UNAWARE_PVID(ds->max_num_bridges))
			vid = 0;

		err = cb(keye->mac_addr, vid, is_static, data);
		if (err)
			break;

		/* To prevent hardware malfunctions from causing an
		 * infinite loop.
		 */
		if (++cnt >= priv->htmcapr_num_words)
			break;
	} while (resume_eid != NTMP_NULL_ENTRY_ID);

	mutex_unlock(&priv->fdbt_lock);
	kfree(entry);

	return err;
}

static int netc_port_mdb_add(struct dsa_switch *ds, int port,
			     const struct switchdev_obj_port_mdb *mdb,
			     struct dsa_db db)
{
	return netc_port_fdb_add(ds, port, mdb->addr, mdb->vid, db);
}

static int netc_port_mdb_del(struct dsa_switch *ds, int port,
			     const struct switchdev_obj_port_mdb *mdb,
			     struct dsa_db db)
{
	return netc_port_fdb_del(ds, port, mdb->addr, mdb->vid, db);
}

static int netc_port_add_host_flood_rule(struct netc_port *np,
					 bool uc, bool mc)
{
	const u8 dmac_mask[ETH_ALEN] = {0x1, 0, 0, 0, 0, 0};
	struct netc_switch *priv = np->switch_priv;
	struct ipft_entry_data *host_flood;
	struct ipft_keye_data *keye;
	struct ipft_cfge_data *cfge;
	u16 src_port;
	u32 cfg;
	int err;

	if (!uc && !mc) {
		/* Disable ingress port filter table lookup */
		netc_port_wr(np, NETC_PIPFCR, 0);
		np->uc = false;
		np->mc = false;

		return 0;
	}

	host_flood = kzalloc_obj(*host_flood);
	if (!host_flood)
		return -ENOMEM;

	keye = &host_flood->keye;
	cfge = &host_flood->cfge;

	src_port = FIELD_PREP(IPFT_SRC_PORT, np->dp->index);
	src_port |= IPFT_SRC_PORT_MASK;
	keye->src_port = cpu_to_le16(src_port);

	/* If either only unicast or only multicast need to be flooded
	 * to the host, we always set the mask that tests the first MAC
	 * DA octet. The value should be 0 for the first bit (if unicast
	 * has to be flooded) or 1 (if multicast). If both unicast and
	 * multicast have to be flooded, we leave the key mask empty, so
	 * it matches everything.
	 */
	if (uc && !mc)
		ether_addr_copy(keye->dmac_mask, dmac_mask);

	if (!uc && mc) {
		ether_addr_copy(keye->dmac, dmac_mask);
		ether_addr_copy(keye->dmac_mask, dmac_mask);
	}

	cfg = FIELD_PREP(IPFT_FLTFA, IPFT_FLTFA_REDIRECT);
	cfg |= FIELD_PREP(IPFT_HR, NETC_HR_HOST_FLOOD);
	cfge->cfg = cpu_to_le32(cfg);

	err = ntmp_ipft_add_entry(&priv->ntmp, host_flood);
	if (err) {
		kfree(host_flood);
		return err;
	}

	np->uc = uc;
	np->mc = mc;
	np->host_flood = host_flood;
	/* Enable ingress port filter table lookup */
	netc_port_wr(np, NETC_PIPFCR, PIPFCR_EN);

	return 0;
}

static void netc_port_remove_host_flood(struct netc_port *np,
					struct ipft_entry_data *host_flood)
{
	struct netc_switch *priv = np->switch_priv;
	bool disable_host_flood = false;

	if (!host_flood)
		return;

	if (np->host_flood == host_flood)
		disable_host_flood = true;

	ntmp_ipft_delete_entry(&priv->ntmp, host_flood->entry_id);
	kfree(host_flood);

	if (disable_host_flood) {
		np->host_flood = NULL;
		np->uc = false;
		np->mc = false;
		netc_port_wr(np, NETC_PIPFCR, 0);
	}
}

static void netc_port_set_host_flood(struct dsa_switch *ds, int port,
				     bool uc, bool mc)
{
	struct netc_port *np = NETC_PORT(ds, port);
	struct ipft_entry_data *old_host_flood;

	/* Do not add host flood rule to ingress port filter table when
	 * the port has joined a bridge. Otherwise, the ingress frames
	 * will bypass FDB table lookup and MAC learning, so the frames
	 * will be redirected directly to the CPU port.
	 */
	if (dsa_port_bridge_dev_get(np->dp)) {
		netc_port_remove_host_flood(np, np->host_flood);

		return;
	}

	if (np->uc == uc && np->mc == mc)
		return;

	/* IPFT does not support in-place updates to the KEYE element,
	 * we need to add a new entry and then delete the old one. So
	 * save the old entry first.
	 */
	old_host_flood = np->host_flood;
	np->host_flood = NULL;

	if (netc_port_add_host_flood_rule(np, uc, mc)) {
		np->host_flood = old_host_flood;
		dev_err(ds->dev, "Failed to add host flood rule on port %d\n",
			port);
		return;
	}

	/* Remove the old host flood entry */
	netc_port_remove_host_flood(np, old_host_flood);
}

static int netc_single_vlan_aware_bridge(struct dsa_switch *ds,
					 struct netlink_ext_ack *extack)
{
	struct net_device *br_ndev = NULL;
	struct dsa_port *dp;

	dsa_switch_for_each_available_port(dp, ds) {
		struct net_device *port_br = dsa_port_bridge_dev_get(dp);

		if (!port_br || !br_vlan_enabled(port_br))
			continue;

		if (!br_ndev) {
			br_ndev = port_br;
			continue;
		}

		if (br_ndev == port_br)
			continue;

		NL_SET_ERR_MSG_MOD(extack,
				   "Only one VLAN-aware bridge is supported");

		return -EBUSY;
	}

	return 0;
}

static int netc_port_vlan_filtering(struct dsa_switch *ds,
				    int port, bool vlan_aware,
				    struct netlink_ext_ack *extack)
{
	struct netc_port *np = NETC_PORT(ds, port);
	u16 pvid;
	int err;

	/* Before calling port_vlan_filtering(), br_vlan_filter_toggle() has
	 * already updated the BROPT_VLAN_ENABLED bit of br->options. So the
	 * VLAN filtering status of the switch ports can be checked by the
	 * br_vlan_enabled() function.
	 */
	err = netc_single_vlan_aware_bridge(ds, extack);
	if (err)
		return err;

	pvid = netc_vlan_unaware_pvid(np->dp->bridge);
	if (pvid == NETC_STANDALONE_PVID) {
		vlan_aware = false;
		goto bpdvr_config;
	}

	if (vlan_aware) {
		/* The FDB entries associated with unaware_pvid do not need
		 * to be deleted, so that when switching from VLAN-aware to
		 * VLAN-unaware mode, these FDB entries do not need to be
		 * re-added.
		 */
		err = netc_port_del_vlan_entry(np, pvid);
		if (err)
			return err;

		pvid = np->pvid;
	} else {
		err = netc_port_set_vlan_entry(np, pvid, false);
		if (err)
			return err;
	}

bpdvr_config:
	netc_port_set_vlan_aware(np, vlan_aware);
	netc_port_set_pvid(np, pvid);

	return 0;
}

static int netc_port_vlan_add(struct dsa_switch *ds, int port,
			      const struct switchdev_obj_port_vlan *vlan,
			      struct netlink_ext_ack *extack)
{
	struct netc_port *np = NETC_PORT(ds, port);
	struct dsa_port *dp = np->dp;
	bool untagged;
	int err;

	/* The 8021q layer may attempt to change NETC_STANDALONE_PVID
	 * (VID 0), so we need to ignore it.
	 */
	if (vlan->vid == NETC_STANDALONE_PVID)
		return 0;

	if (vlan->vid >= NETC_VLAN_UNAWARE_PVID(ds->max_num_bridges)) {
		NL_SET_ERR_MSG_FMT_MOD(extack,
				       "VID %d~4095 reserved for VLAN-unaware bridge",
				       NETC_VLAN_UNAWARE_PVID(ds->max_num_bridges));
		return -EINVAL;
	}

	untagged = !!(vlan->flags & BRIDGE_VLAN_INFO_UNTAGGED);
	err = netc_port_set_vlan_entry(np, vlan->vid, untagged);
	if (err)
		return err;

	if (vlan->flags & BRIDGE_VLAN_INFO_PVID) {
		np->pvid = vlan->vid;
		if (dsa_port_is_vlan_filtering(dp))
			netc_port_set_pvid(np, vlan->vid);

		return 0;
	}

	if (np->pvid != vlan->vid)
		return 0;

	/* Delete PVID */
	np->pvid = NETC_STANDALONE_PVID;
	if (dsa_port_is_vlan_filtering(dp))
		netc_port_set_pvid(np, NETC_STANDALONE_PVID);

	return 0;
}

static int netc_port_vlan_del(struct dsa_switch *ds, int port,
			      const struct switchdev_obj_port_vlan *vlan)
{
	struct netc_port *np = NETC_PORT(ds, port);
	int err;

	if (vlan->vid == NETC_STANDALONE_PVID)
		return 0;

	if (vlan->vid >= NETC_VLAN_UNAWARE_PVID(ds->max_num_bridges))
		return -EINVAL;

	err = netc_port_del_vlan_entry(np, vlan->vid);
	if (err)
		return err;

	if (np->pvid == vlan->vid) {
		np->pvid = NETC_STANDALONE_PVID;

		/* Set the port PVID to NETC_STANDALONE_PVID if the VLAN-aware
		 * bridge port has no PVID. The untagged frames will not be
		 * forwarded to other user ports, as NETC_STANDALONE_PVID VLAN
		 * entry has disabled MAC learning and flooding, and other user
		 * ports do not have FDB entries with NETC_STANDALONE_PVID.
		 */
		if (dsa_port_is_vlan_filtering(np->dp))
			netc_port_set_pvid(np, NETC_STANDALONE_PVID);
	}

	return 0;
}

static int netc_port_bridge_join(struct dsa_switch *ds, int port,
				 struct dsa_bridge bridge,
				 bool *tx_fwd_offload,
				 struct netlink_ext_ack *extack)
{
	struct netc_port *np = NETC_PORT(ds, port);
	struct netc_switch *priv = ds->priv;
	u16 vlan_unaware_pvid;
	int err;

	if (!bridge.num) {
		NL_SET_ERR_MSG_MOD(extack, "Bridge number 0 is unsupported");
		return -EINVAL;
	}

	err = netc_single_vlan_aware_bridge(ds, extack);
	if (err)
		return err;

	netc_port_set_mlo(np, MLO_NOT_OVERRIDE);

	if (br_vlan_enabled(bridge.dev))
		goto out;

	vlan_unaware_pvid = NETC_VLAN_UNAWARE_PVID(bridge.num);
	err = netc_port_set_vlan_entry(np, vlan_unaware_pvid, false);
	if (err)
		goto disable_mlo;

	netc_port_set_pvid(np, vlan_unaware_pvid);

out:
	netc_port_remove_host_flood(np, np->host_flood);

	if (atomic_inc_return(&priv->br_cnt) == 1)
		schedule_delayed_work(&priv->fdbt_ageing_work,
				      READ_ONCE(priv->fdbt_ageing_delay));

	return 0;

disable_mlo:
	netc_port_set_mlo(np, MLO_DISABLE);

	return err;
}

static void netc_port_remove_dynamic_entries(struct netc_port *np)
{
	struct netc_switch *priv = np->switch_priv;

	/* Return if the port is not available */
	if (!np->dp)
		return;

	mutex_lock(&priv->fdbt_lock);
	ntmp_fdbt_delete_port_dynamic_entries(&priv->ntmp, np->dp->index);
	mutex_unlock(&priv->fdbt_lock);
}

static void netc_port_bridge_leave(struct dsa_switch *ds, int port,
				   struct dsa_bridge bridge)
{
	struct netc_port *np = NETC_PORT(ds, port);
	struct net_device *ndev = np->dp->user;
	struct netc_switch *priv = ds->priv;
	u16 vlan_unaware_pvid;
	bool mc, uc;

	netc_port_set_mlo(np, MLO_DISABLE);
	netc_port_set_pvid(np, NETC_STANDALONE_PVID);
	np->pvid = NETC_STANDALONE_PVID;

	if (atomic_dec_and_test(&priv->br_cnt))
		cancel_delayed_work_sync(&priv->fdbt_ageing_work);

	netc_port_remove_dynamic_entries(np);
	uc = ndev->flags & IFF_PROMISC;
	mc = ndev->flags & (IFF_PROMISC | IFF_ALLMULTI);

	if (netc_port_add_host_flood_rule(np, uc, mc))
		dev_warn(ds->dev,
			 "Failed to restore host flood rule on port %d\n",
			 port);

	/* When a port leaves a VLAN-aware bridge, dsa_port_bridge_leave()
	 * follows the sequence below:
	 *
	 * 1. dsa_port_bridge_destroy() is called to set dp->bridge to NULL.
	 * 2. dsa_broadcast() is called, which eventually invokes
	 *    ds->ops->port_bridge_leave()
	 * 3. dsa_port_switchdev_unsync_attrs() is called, which triggers
	 *    dsa_port_reset_vlan_filtering() and ultimately calls
	 *    ds->ops->port_vlan_filtering() to transition the port from
	 *    VLAN-aware mode to VLAN-unaware mode.
	 *
	 * At step 3, since dp->bridge has already been set to NULL in step 1,
	 * netc_port_vlan_filtering() will detect this and skip the creation
	 * of an unaware PVID entry in the VLAN filter table. Therefore, it is
	 * safe to return directly here.
	 */
	if (br_vlan_enabled(bridge.dev))
		return;

	vlan_unaware_pvid = NETC_VLAN_UNAWARE_PVID(bridge.num);
	/* There is no need to check the return value even if it fails.
	 * Because the PVID has been set to NETC_STANDALONE_PVID, the
	 * frames will not match this VLAN entry.
	 */
	netc_port_del_vlan_entry(np, vlan_unaware_pvid);
}

static int netc_set_ageing_time(struct dsa_switch *ds, unsigned int msecs)
{
	struct netc_switch *priv = ds->priv;
	unsigned long delay_jiffies;

	/* The dynamic FDB entry is deleted when its activity counter reaches
	 * NETC_FDBT_AGEING_THRESH (100). Each delayed_work tick increments
	 * the counter by 1 if the entry is inactive.
	 *
	 * Therefore:
	 *   msecs (ms)    = NETC_FDBT_AGEING_THRESH * delay_ms (ms)
	 *   delay_ms      = msecs / NETC_FDBT_AGEING_THRESH
	 *   delay_jiffies = (delay_ms / 1000) * HZ
	 *                 = (msecs * HZ) / (1000 * NETC_FDBT_AGEING_THRESH)
	 *
	 * Use DIV_ROUND_CLOSEST_ULL to perform a single nearest-jiffy
	 * rounding, avoiding the two-step rounding error of the intermediate
	 * delay_ms approach.
	 *   Maximum error = +/-0.5 jiffy * 100 = +/-50000/HZ ms.
	 */
	delay_jiffies = DIV_ROUND_CLOSEST_ULL((u64)msecs * HZ,
					      1000 * NETC_FDBT_AGEING_THRESH);
	WRITE_ONCE(priv->fdbt_ageing_delay, delay_jiffies);

	if (atomic_read(&priv->br_cnt))
		mod_delayed_work(system_percpu_wq, &priv->fdbt_ageing_work,
				 READ_ONCE(priv->fdbt_ageing_delay));

	return 0;
}

static void netc_port_fast_age(struct dsa_switch *ds, int port)
{
	struct netc_port *np = NETC_PORT(ds, port);

	netc_port_remove_dynamic_entries(np);
}

static void netc_phylink_get_caps(struct dsa_switch *ds, int port,
				  struct phylink_config *config)
{
	struct netc_switch *priv = ds->priv;

	priv->info->phylink_get_caps(port, config);
}

static void netc_port_set_mac_mode(struct netc_port *np,
				   unsigned int mode,
				   phy_interface_t phy_mode)
{
	u32 mask = PM_IF_MODE_IFMODE | PM_IF_MODE_REVMII;
	u32 val = 0;

	switch (phy_mode) {
	case PHY_INTERFACE_MODE_RGMII:
	case PHY_INTERFACE_MODE_RGMII_ID:
	case PHY_INTERFACE_MODE_RGMII_RXID:
	case PHY_INTERFACE_MODE_RGMII_TXID:
		val |= IFMODE_RGMII;
		break;
	case PHY_INTERFACE_MODE_RMII:
		val |= IFMODE_RMII;
		break;
	case PHY_INTERFACE_MODE_REVMII:
		val |= PM_IF_MODE_REVMII;
		fallthrough;
	case PHY_INTERFACE_MODE_MII:
		val |= IFMODE_MII;
		break;
	case PHY_INTERFACE_MODE_SGMII:
	case PHY_INTERFACE_MODE_2500BASEX:
		val |= IFMODE_SGMII;
		break;
	default:
		break;
	}

	netc_mac_port_rmw(np, NETC_PM_IF_MODE(0), mask, val);
}

static void netc_mac_config(struct phylink_config *config, unsigned int mode,
			    const struct phylink_link_state *state)
{
	struct dsa_port *dp = dsa_phylink_to_port(config);

	netc_port_set_mac_mode(NETC_PORT(dp->ds, dp->index), mode,
			       state->interface);
}

static void netc_port_set_speed(struct netc_port *np, int speed)
{
	netc_port_rmw(np, NETC_PCR, PCR_PSPEED, PSPEED_SET_VAL(speed));
}

static void netc_port_set_rgmii_mac(struct netc_port *np,
				    int speed, int duplex)
{
	u32 mask, val;

	mask = PM_IF_MODE_SSP | PM_IF_MODE_HD | PM_IF_MODE_M10;

	switch (speed) {
	default:
	case SPEED_1000:
		val = FIELD_PREP(PM_IF_MODE_SSP, SSP_1G);
		break;
	case SPEED_100:
		val = FIELD_PREP(PM_IF_MODE_SSP, SSP_100M);
		break;
	case SPEED_10:
		val = FIELD_PREP(PM_IF_MODE_SSP, SSP_10M);
		break;
	}

	if (duplex != DUPLEX_FULL)
		val |= PM_IF_MODE_HD;

	netc_mac_port_rmw(np, NETC_PM_IF_MODE(0), mask, val);
}

static void netc_port_set_rmii_mii_mac(struct netc_port *np,
				       int speed, int duplex)
{
	u32 mask, val = 0;

	mask = PM_IF_MODE_SSP | PM_IF_MODE_HD | PM_IF_MODE_M10;

	if (speed == SPEED_10)
		val |= PM_IF_MODE_M10;

	if (duplex != DUPLEX_FULL)
		val |= PM_IF_MODE_HD;

	netc_mac_port_rmw(np, NETC_PM_IF_MODE(0), mask, val);
}

static void netc_port_set_tx_pause(struct netc_port *np, bool tx_pause)
{
	struct netc_switch *priv = np->switch_priv;
	int port = np->dp->index;
	int i, j, num_bp;

	num_bp = priv->num_bp / priv->info->num_ports;
	for (i = 0, j = port * num_bp; i < num_bp; i++, j++) {
		struct bpt_cfge_data *cfge = &priv->bpt_list[j];
		struct bpt_cfge_data old_cfge = *cfge;

		if (tx_pause) {
			cfge->fc_on_thresh = cpu_to_le16(NETC_FC_THRESH_ON);
			cfge->fc_off_thresh = cpu_to_le16(NETC_FC_THRESH_OFF);
			cfge->fccfg_sbpen = FIELD_PREP(BPT_FC_CFG,
						       BPT_FC_CFG_EN_BPFC);
			cfge->fc_ports = cpu_to_le32(BIT(port));
		} else {
			cfge->fc_on_thresh = cpu_to_le16(0);
			cfge->fc_off_thresh = cpu_to_le16(0);
			cfge->fccfg_sbpen = 0;
			cfge->fc_ports = cpu_to_le32(0);
		}

		if (ntmp_bpt_update_entry(&priv->ntmp, j, cfge)) {
			*cfge = old_cfge;
			dev_warn(priv->dev,
				 "Failed to %s TX pause of buffer pool %d (swp%d)\n",
				 tx_pause ? "enable" : "disable", j, port);
		}
	}
}

static void netc_port_set_rx_pause(struct netc_port *np, bool rx_pause)
{
	netc_mac_port_rmw(np, NETC_PM_CMD_CFG(0), PM_CMD_CFG_PAUSE_IGN,
			  rx_pause ? 0 : PM_CMD_CFG_PAUSE_IGN);
}

static void netc_port_mac_rx_enable(struct netc_port *np)
{
	netc_port_rmw(np, NETC_POR, POR_RXDIS, 0);
	netc_mac_port_rmw(np, NETC_PM_CMD_CFG(0), PM_CMD_CFG_RX_EN,
			  PM_CMD_CFG_RX_EN);
}

static void netc_port_wait_rx_empty(struct netc_port *np, int mac)
{
	u32 val;

	/* PM_IEVENT_RX_EMPTY is a read-only bit, it is automatically set by
	 * hardware if RX FIFO is empty and no RX packet receive in process.
	 * And it is automatically cleared if RX FIFO is not empty or RX
	 * packet receive in process.
	 */
	if (read_poll_timeout(netc_port_rd, val, val & PM_IEVENT_RX_EMPTY,
			      100, 10000, false, np, NETC_PM_IEVENT(mac)))
		dev_warn(np->switch_priv->dev,
			 "swp%d MAC%d: RX is not idle\n", np->dp->index, mac);
}

static void netc_port_mac_rx_graceful_stop(struct netc_port *np)
{
	u32 val;

	if (is_netc_pseudo_port(np))
		goto rx_disable;

	if (np->caps.pmac) {
		netc_port_rmw(np, NETC_PM_CMD_CFG(1), PM_CMD_CFG_RX_EN, 0);
		netc_port_wait_rx_empty(np, 1);
	}

	netc_port_rmw(np, NETC_PM_CMD_CFG(0), PM_CMD_CFG_RX_EN, 0);
	netc_port_wait_rx_empty(np, 0);

	if (read_poll_timeout(netc_port_rd, val, !(val & PSR_RX_BUSY),
			      100, 10000, false, np, NETC_PSR))
		dev_warn(np->switch_priv->dev, "swp%d RX is busy\n",
			 np->dp->index);

rx_disable:
	netc_port_rmw(np, NETC_POR, POR_RXDIS, POR_RXDIS);
}

static void netc_port_mac_tx_enable(struct netc_port *np)
{
	netc_mac_port_rmw(np, NETC_PM_CMD_CFG(0), PM_CMD_CFG_TX_EN,
			  PM_CMD_CFG_TX_EN);
	netc_port_rmw(np, NETC_POR, POR_TXDIS, 0);
}

static void netc_port_wait_tx_empty(struct netc_port *np, int mac)
{
	u32 val;

	/* PM_IEVENT_TX_EMPTY is a read-only bit, it is automatically set by
	 * hardware if TX FIFO is empty. And it is automatically cleared if
	 * TX FIFO is not empty.
	 */
	if (read_poll_timeout(netc_port_rd, val, val & PM_IEVENT_TX_EMPTY,
			      100, 10000, false, np, NETC_PM_IEVENT(mac)))
		dev_warn(np->switch_priv->dev,
			 "swp%d MAC%d: TX FIFO is not empty\n",
			 np->dp->index, mac);
}

static void netc_port_mac_tx_graceful_stop(struct netc_port *np)
{
	netc_port_rmw(np, NETC_POR, POR_TXDIS, POR_TXDIS);

	if (is_netc_pseudo_port(np))
		return;

	netc_port_wait_tx_empty(np, 0);
	if (np->caps.pmac)
		netc_port_wait_tx_empty(np, 1);

	netc_mac_port_rmw(np, NETC_PM_CMD_CFG(0), PM_CMD_CFG_TX_EN, 0);
}

static void netc_mac_link_up(struct phylink_config *config,
			     struct phy_device *phy, unsigned int mode,
			     phy_interface_t interface, int speed,
			     int duplex, bool tx_pause, bool rx_pause)
{
	struct dsa_port *dp = dsa_phylink_to_port(config);
	struct netc_port *np;

	np = NETC_PORT(dp->ds, dp->index);
	netc_port_set_speed(np, speed);

	if (phy_interface_mode_is_rgmii(interface))
		netc_port_set_rgmii_mac(np, speed, duplex);

	if (interface == PHY_INTERFACE_MODE_RMII ||
	    interface == PHY_INTERFACE_MODE_REVMII ||
	    interface == PHY_INTERFACE_MODE_MII)
		netc_port_set_rmii_mii_mac(np, speed, duplex);

	netc_port_set_tx_pause(np, tx_pause);
	netc_port_set_rx_pause(np, rx_pause);
	netc_port_mac_tx_enable(np);
	netc_port_mac_rx_enable(np);
}

static void netc_mac_link_down(struct phylink_config *config,
			       unsigned int mode,
			       phy_interface_t interface)
{
	struct dsa_port *dp = dsa_phylink_to_port(config);
	struct netc_port *np;

	np = NETC_PORT(dp->ds, dp->index);
	netc_port_mac_rx_graceful_stop(np);
	netc_port_mac_tx_graceful_stop(np);
	netc_port_remove_dynamic_entries(np);
}

static const struct phylink_mac_ops netc_phylink_mac_ops = {
	.mac_config		= netc_mac_config,
	.mac_link_up		= netc_mac_link_up,
	.mac_link_down		= netc_mac_link_down,
};

static const struct dsa_switch_ops netc_switch_ops = {
	.get_tag_protocol		= netc_get_tag_protocol,
	.setup				= netc_setup,
	.teardown			= netc_teardown,
	.phylink_get_caps		= netc_phylink_get_caps,
	.port_enable			= netc_port_enable,
	.port_disable			= netc_port_disable,
	.port_stp_state_set		= netc_port_stp_state_set,
	.port_change_mtu		= netc_port_change_mtu,
	.port_max_mtu			= netc_port_max_mtu,
	.port_fdb_add			= netc_port_fdb_add,
	.port_fdb_del			= netc_port_fdb_del,
	.port_fdb_dump			= netc_port_fdb_dump,
	.port_mdb_add			= netc_port_mdb_add,
	.port_mdb_del			= netc_port_mdb_del,
	.port_set_host_flood		= netc_port_set_host_flood,
	.port_vlan_filtering		= netc_port_vlan_filtering,
	.port_vlan_add			= netc_port_vlan_add,
	.port_vlan_del			= netc_port_vlan_del,
	.port_bridge_join		= netc_port_bridge_join,
	.port_bridge_leave		= netc_port_bridge_leave,
	.set_ageing_time		= netc_set_ageing_time,
	.port_fast_age			= netc_port_fast_age,
	.get_pause_stats		= netc_port_get_pause_stats,
	.get_rmon_stats			= netc_port_get_rmon_stats,
	.get_eth_ctrl_stats		= netc_port_get_eth_ctrl_stats,
	.get_eth_mac_stats		= netc_port_get_eth_mac_stats,
	.get_sset_count			= netc_port_get_sset_count,
	.get_strings			= netc_port_get_strings,
	.get_ethtool_stats		= netc_port_get_ethtool_stats,
};

static int netc_switch_probe(struct pci_dev *pdev,
			     const struct pci_device_id *id)
{
	struct device_node *node = dev_of_node(&pdev->dev);
	struct device *dev = &pdev->dev;
	struct netc_switch *priv;
	struct dsa_switch *ds;
	int err;

	if (!node)
		return dev_err_probe(dev, -ENODEV,
				     "No DT bindings, skipping\n");

	err = netc_switch_check_emdio_is_ready(dev);
	if (err)
		return err;

	err = netc_switch_pci_init(pdev);
	if (err)
		return err;

	priv = pci_get_drvdata(pdev);
	netc_switch_get_ip_revision(priv);

	err = netc_switch_platform_probe(priv);
	if (err)
		return err;

	ds = devm_kzalloc(dev, sizeof(*ds), GFP_KERNEL);
	if (!ds)
		return -ENOMEM;

	ds->dev = dev;
	ds->num_ports = priv->info->num_ports;
	ds->num_tx_queues = NETC_TC_NUM;
	ds->ops = &netc_switch_ops;
	ds->phylink_mac_ops = &netc_phylink_mac_ops;
	ds->fdb_isolation = true;
	ds->max_num_bridges = priv->info->num_ports - 1;
	ds->ageing_time_min = 1000;
	ds->ageing_time_max = U32_MAX;
	ds->priv = priv;
	priv->ds = ds;

	err = dsa_register_switch(ds);
	if (err)
		return dev_err_probe(dev, err,
				     "Failed to register DSA switch\n");

	return 0;
}

static void netc_switch_remove(struct pci_dev *pdev)
{
	struct netc_switch *priv = pci_get_drvdata(pdev);

	if (!priv)
		return;

	dsa_unregister_switch(priv->ds);
}

static void netc_switch_shutdown(struct pci_dev *pdev)
{
	struct netc_switch *priv = pci_get_drvdata(pdev);

	if (!priv)
		return;

	dsa_switch_shutdown(priv->ds);
	pci_set_drvdata(pdev, NULL);
}

static const struct pci_device_id netc_switch_ids[] = {
	{ PCI_DEVICE(NETC_SWITCH_VENDOR_ID, NETC_SWITCH_DEVICE_ID) },
	{ }
};
MODULE_DEVICE_TABLE(pci, netc_switch_ids);

static struct pci_driver netc_switch_driver = {
	.name		= KBUILD_MODNAME,
	.id_table	= netc_switch_ids,
	.probe		= netc_switch_probe,
	.remove		= netc_switch_remove,
	.shutdown	= netc_switch_shutdown,
};
module_pci_driver(netc_switch_driver);

MODULE_DESCRIPTION("NXP NETC Switch driver");
MODULE_LICENSE("Dual BSD/GPL");