linux/drivers/net/dsa/netc/netc_main.c
Wei Fang 05b5ee610f net: dsa: netc: implement dynamic FDB entry ageing
The NETC switch does not age out dynamic FDB entries automatically.
Without software management, stale entries persist after topology
changes and cause incorrect forwarding.

Add a delayed work that periodically removes entries that have not been
refreshed within the specified cycles. The effective ageing time is:

  ageing_time = fdbt_ageing_delay * 100

Default values are 3s interval and 100 cycles (300s total), matching
the IEEE 802.1Q default ageing time. The work starts when the first
port joins a bridge (tracked via br_cnt) and is cancelled when the
last port leaves. All FDB operations are serialized under fdbt_lock.

Implement .set_ageing_time() to allow the bridge layer to reconfigure
ageing parameters on demand.

Signed-off-by: Wei Fang <wei.fang@nxp.com>
Link: https://patch.msgid.link/20260611021458.2629145-10-wei.fang@oss.nxp.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
2026-06-15 14:32:06 -07:00

2525 lines
63 KiB
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");