// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) /* * NXP NETC switch driver * Copyright 2025-2026 NXP */ #include #include #include #include #include #include #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");