linux/drivers/net/dsa/netc/netc_switch.h
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

207 lines
5.5 KiB
C

/* SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause) */
/*
* Copyright 2025-2026 NXP
*/
#ifndef _NETC_SWITCH_H
#define _NETC_SWITCH_H
#include <linux/dsa/tag_netc.h>
#include <linux/fsl/netc_global.h>
#include <linux/fsl/ntmp.h>
#include <linux/of_device.h>
#include <linux/of_net.h>
#include <linux/pci.h>
#include "netc_switch_hw.h"
#define NETC_REGS_BAR 0
#define NETC_REGS_SIZE 0x80000
#define NETC_MSIX_TBL_BAR 2
#define NETC_REGS_PORT_BASE 0x4000
/* register block size per port */
#define NETC_REGS_PORT_SIZE 0x4000
#define PORT_IOBASE(p) (NETC_REGS_PORT_SIZE * (p))
#define NETC_REGS_GLOBAL_BASE 0x70000
#define NETC_SWITCH_REV_4_3 0x0403
#define NETC_TC_NUM 8
#define NETC_CBDR_NUM 2
#define NETC_IPV_NUM 8
#define NETC_MAX_FRAME_LEN 9600
#define NETC_STANDALONE_PVID 0
#define NETC_VLAN_UNAWARE_PVID(br_id) (4096 - (br_id))
/* Threshold format: MANT (bits 11:4) * 2^EXP (bits 3:0)
* Unit: Memory words (average of 20 bytes each)
* NETC_BP_THRESH = 0x8c3, MANT = 0x8c, EXP = 3. Threshold: 1120 words
* NETC_FC_THRESH_ON = 0x733, MANT = 0x73, EXP = 3. Threshold: 920 words
* NETC_FC_THRESH_OFF = 0x263, MANT = 0x26, EXP = 3. Threshold: 304 words
*/
#define NETC_BP_THRESH 0x8c3
#define NETC_FC_THRESH_ON 0x733
#define NETC_FC_THRESH_OFF 0x263
/* PAUSE quanta: 0xFFFF = 65535 quanta (each quanta = 512 bit times) */
#define NETC_PAUSE_QUANTA 0xFFFF
/* PAUSE refresh threshold: send refresh when timer reaches this value */
#define NETC_PAUSE_THRESH 0x7FFF
#define NETC_FDBT_AGEING_DELAY (3 * HZ)
#define NETC_FDBT_AGEING_THRESH 100
struct netc_switch;
struct netc_switch_info {
u32 num_ports;
void (*phylink_get_caps)(int port, struct phylink_config *config);
};
struct netc_port_caps {
u32 half_duplex:1; /* indicates whether the port support half-duplex */
u32 pmac:1; /* indicates whether the port has preemption MAC */
u32 pseudo_link:1;
};
enum netc_host_reason {
/* Software defined host reasons */
NETC_HR_HOST_FLOOD = 8,
};
struct netc_port {
void __iomem *iobase;
struct netc_switch *switch_priv;
struct netc_port_caps caps;
struct dsa_port *dp;
struct clk *ref_clk; /* RGMII/RMII reference clock */
struct mii_bus *emdio;
int ett_offset;
u16 enable:1;
u16 uc:1;
u16 mc:1;
u16 pvid;
struct ipft_entry_data *host_flood;
};
struct netc_switch_regs {
void __iomem *base;
void __iomem *port;
void __iomem *global;
};
struct netc_fdb_entry {
u32 entry_id;
struct fdbt_cfge_data cfge;
struct fdbt_keye_data keye;
struct hlist_node node;
};
struct netc_vlan_entry {
u16 vid;
u32 ect_gid;
u32 untagged_port_bitmap;
struct vft_cfge_data cfge;
struct hlist_node node;
};
struct netc_port_stat {
int reg;
char name[ETH_GSTRING_LEN] __nonstring;
};
struct netc_switch {
struct pci_dev *pdev;
struct device *dev;
struct dsa_switch *ds;
u16 revision;
const struct netc_switch_info *info;
struct netc_switch_regs regs;
struct netc_port **ports;
u32 port_bitmap; /* bitmap of available ports */
struct ntmp_user ntmp;
struct hlist_head fdb_list;
struct mutex fdbt_lock; /* FDB table lock */
struct delayed_work fdbt_ageing_work;
/* (fdbt_ageing_delay * NETC_FDBT_AGEING_THRESH) is ageing time */
unsigned long fdbt_ageing_delay;
atomic_t br_cnt;
struct hlist_head vlan_list;
struct mutex vft_lock; /* VLAN filter table lock */
/* Switch hardware capabilities */
u32 htmcapr_num_words;
u32 num_bp;
struct bpt_cfge_data *bpt_list;
};
#define NETC_PRIV(ds) ((struct netc_switch *)((ds)->priv))
#define NETC_PORT(ds, port_id) (NETC_PRIV(ds)->ports[(port_id)])
/* Write/Read Switch base registers */
#define netc_base_rd(r, o) netc_read((r)->base + (o))
#define netc_base_wr(r, o, v) netc_write((r)->base + (o), v)
/* Write/Read registers of Switch Port (including pseudo MAC port) */
#define netc_port_rd(p, o) netc_read((p)->iobase + (o))
#define netc_port_rd64(p, o) netc_read64((p)->iobase + (o))
#define netc_port_wr(p, o, v) netc_write((p)->iobase + (o), v)
/* Write/Read Switch global registers */
#define netc_glb_rd(r, o) netc_read((r)->global + (o))
#define netc_glb_wr(r, o, v) netc_write((r)->global + (o), v)
static inline bool is_netc_pseudo_port(struct netc_port *np)
{
return np->caps.pseudo_link;
}
static inline void netc_add_fdb_entry(struct netc_switch *priv,
struct netc_fdb_entry *entry)
{
hlist_add_head(&entry->node, &priv->fdb_list);
}
static inline void netc_del_fdb_entry(struct netc_fdb_entry *entry)
{
hlist_del(&entry->node);
kfree(entry);
}
static inline void netc_add_vlan_entry(struct netc_switch *priv,
struct netc_vlan_entry *entry)
{
hlist_add_head(&entry->node, &priv->vlan_list);
}
static inline void netc_del_vlan_entry(struct netc_vlan_entry *entry)
{
hlist_del(&entry->node);
kfree(entry);
}
int netc_switch_platform_probe(struct netc_switch *priv);
/* ethtool APIs */
void netc_port_get_pause_stats(struct dsa_switch *ds, int port,
struct ethtool_pause_stats *pause_stats);
void netc_port_get_rmon_stats(struct dsa_switch *ds, int port,
struct ethtool_rmon_stats *rmon_stats,
const struct ethtool_rmon_hist_range **ranges);
void netc_port_get_eth_ctrl_stats(struct dsa_switch *ds, int port,
struct ethtool_eth_ctrl_stats *ctrl_stats);
void netc_port_get_eth_mac_stats(struct dsa_switch *ds, int port,
struct ethtool_eth_mac_stats *mac_stats);
int netc_port_get_sset_count(struct dsa_switch *ds, int port, int sset);
void netc_port_get_strings(struct dsa_switch *ds, int port,
u32 sset, u8 *data);
void netc_port_get_ethtool_stats(struct dsa_switch *ds, int port, u64 *data);
#endif