root/drivers/net/dsa/netc/netc_main.c
// 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");