mirror of
https://github.com/torvalds/linux.git
synced 2026-07-28 01:55:51 +02:00
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>
207 lines
5.5 KiB
C
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
|