net: dsa: felix: add tsn support for felix switch based on net/tsn

VSC9959 has hardware TSN features. Allow the following to be configured
through the tsntool netlink interface:
- IEEE 802.1Qbv
- IEEE 802.1Qbu/802.3br
- IEEE 802.1Qci
- IEEE 802.1Qav
- IEEE 802.1CB

This patch is based on netlink adaptation layer in net/tsn/*.
The functionality is automatically enabled if CONFIG_TSN=y or m.

Signed-off-by: Xiaoliang Yang <xiaoliang.yang_1@nxp.com>
Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
This commit is contained in:
Vladimir Oltean 2023-03-09 16:43:17 +02:00
parent aac0120422
commit 4d63133f7d
12 changed files with 1674 additions and 30 deletions

View File

@ -37,6 +37,7 @@ config NET_DSA_MSCC_FELIX
depends on HAS_IOMEM
depends on PTP_1588_CLOCK_OPTIONAL
depends on NET_SCH_TAPRIO || NET_SCH_TAPRIO=n
depends on TSN || TSN=n
select MSCC_OCELOT_SWITCH_LIB
select NET_DSA_MSCC_FELIX_DSA_LIB
select NET_DSA_TAG_OCELOT_8021Q

View File

@ -8,3 +8,7 @@ mscc_felix_dsa_lib-objs := felix.o
mscc_felix-objs := felix_vsc9959.o
mscc_ocelot_ext-objs := ocelot_ext.o
mscc_seville-objs := seville_vsc9953.o
ifdef CONFIG_TSN
mscc_felix-objs += felix_tsn.o
endif

View File

@ -1454,6 +1454,10 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)
ocelot_port->ocelot = ocelot;
ocelot_port->target = target;
ocelot_port->index = port;
/* Enable cut-through forwarding on all traffic classes by
* default, to be compatible with the upstream kernel.
*/
ocelot_port->cut_thru = GENMASK(7, 0);
ocelot->ports[port] = ocelot_port;
}

View File

@ -102,5 +102,7 @@ struct felix {
struct net_device *felix_port_to_netdev(struct ocelot *ocelot, int port);
int felix_netdev_to_port(struct net_device *dev);
void vsc9959_new_base_time(struct ocelot *ocelot, ktime_t base_time,
u64 cycle_time, struct timespec64 *new_base_ts);
#endif

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,32 @@
/* SPDX-License-Identifier: (GPL-2.0 OR MIT)
*
* TSN support for Felix VSC9959 through tsntool
*
* Copyright 2020-2023 NXP
*/
#ifndef _MSCC_FELIX_SWITCH_TSN_H_
#define _MSCC_FELIX_SWITCH_TSN_H_
#include <soc/mscc/ocelot.h>
#include <net/dsa.h>
#if IS_ENABLED(CONFIG_TSN)
void felix_cbs_reset(struct ocelot *ocelot, int port, u32 speed);
void felix_tsn_enable(struct dsa_switch *ds);
#else
static inline void felix_cbs_reset(struct ocelot *ocelot, int port,
u32 speed)
{
}
static inline void felix_tsn_enable(struct dsa_switch *ds)
{
}
#endif
#endif

View File

@ -20,6 +20,7 @@
#include <linux/of.h>
#include <linux/pci.h>
#include <linux/time.h>
#include "felix_tsn.h"
#include "felix.h"
#define VSC9959_NUM_PORTS 6
@ -1379,11 +1380,12 @@ static void vsc9959_sched_speed_set(struct ocelot *ocelot, int port,
vsc9959_tas_guard_bands_update(ocelot, port);
mutex_unlock(&ocelot->fwd_domain_lock);
felix_cbs_reset(ocelot, port, speed);
}
static void vsc9959_new_base_time(struct ocelot *ocelot, ktime_t base_time,
u64 cycle_time,
struct timespec64 *new_base_ts)
void vsc9959_new_base_time(struct ocelot *ocelot, ktime_t base_time,
u64 cycle_time, struct timespec64 *new_base_ts)
{
struct timespec64 ts;
ktime_t new_base_time;
@ -2585,7 +2587,7 @@ static void vsc9959_cut_through_fwd(struct ocelot *ocelot)
* reason, if sent as cut-through.
*/
if (ocelot_port->speed == min_speed) {
val = GENMASK(7, 0) & ~mm->active_preemptible_tcs;
val = ocelot_port->cut_thru & ~mm->active_preemptible_tcs;
for (tc = 0; tc < OCELOT_NUM_TC; tc++)
if (vsc9959_port_qmaxsdu_get(ocelot, port, tc))
@ -2728,6 +2730,8 @@ static int felix_pci_probe(struct pci_dev *pdev,
goto err_register_ds;
}
felix_tsn_enable(ds);
return 0;
err_register_ds:

View File

@ -21,12 +21,6 @@
#define OCELOT_RSV_VLAN_RANGE_START 4000
struct ocelot_mact_entry {
u8 mac[ETH_ALEN];
u16 vid;
enum macaccess_entry_type type;
};
/* Caller must hold &ocelot->mact_lock */
static inline u32 ocelot_mact_read_macaccess(struct ocelot *ocelot)
{
@ -1259,10 +1253,10 @@ int ocelot_fdb_del(struct ocelot *ocelot, int port, const unsigned char *addr,
EXPORT_SYMBOL(ocelot_fdb_del);
/* Caller must hold &ocelot->mact_lock */
static int ocelot_mact_read(struct ocelot *ocelot, int port, int row, int col,
struct ocelot_mact_entry *entry)
int ocelot_mact_read(struct ocelot *ocelot, int row, int col, int *dst,
struct ocelot_mact_entry *entry)
{
u32 val, dst, macl, mach;
u32 val, macl, mach;
char mac[ETH_ALEN];
/* Set row and column to read from */
@ -1282,12 +1276,9 @@ static int ocelot_mact_read(struct ocelot *ocelot, int port, int row, int col,
if (!(val & ANA_TABLES_MACACCESS_VALID))
return -EINVAL;
/* If the entry read has another port configured as its destination,
* do not report it.
*/
dst = (val & ANA_TABLES_MACACCESS_DEST_IDX_M) >> 3;
if (dst != port)
return -EINVAL;
*dst = (val & ANA_TABLES_MACACCESS_DEST_IDX_M) >> 3;
entry->type = ANA_TABLES_MACACCESS_ENTRYTYPE_X(val);
/* Get the entry's MAC address and VLAN id */
macl = ocelot_read(ocelot, ANA_TABLES_MACLDATA);
@ -1305,6 +1296,7 @@ static int ocelot_mact_read(struct ocelot *ocelot, int port, int row, int col,
return 0;
}
EXPORT_SYMBOL(ocelot_mact_read);
int ocelot_mact_flush(struct ocelot *ocelot, int port)
{
@ -1359,16 +1351,18 @@ int ocelot_fdb_dump(struct ocelot *ocelot, int port,
for (j = 0; j < 4; j++) {
struct ocelot_mact_entry entry;
bool is_static;
int dst;
err = ocelot_mact_read(ocelot, port, i, j, &entry);
/* If the entry is invalid (wrong port, invalid...),
* skip it.
*/
err = ocelot_mact_read(ocelot, i, j, &dst, &entry);
/* If the entry is invalid, skip it. */
if (err == -EINVAL)
continue;
else if (err)
break;
if (dst != port)
continue;
is_static = (entry.type == ENTRYTYPE_LOCKED);
/* Hide the reserved VLANs used for
@ -1721,6 +1715,42 @@ void ocelot_port_unassign_dsa_8021q_cpu(struct ocelot *ocelot, int port)
}
EXPORT_SYMBOL_GPL(ocelot_port_unassign_dsa_8021q_cpu);
void ocelot_bridge_force_forward_port(struct ocelot *ocelot, int port, bool en)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
u32 mask;
int i;
mutex_lock(&ocelot->fwd_domain_lock);
if (!en) {
if (ocelot_port->force_forward) {
ocelot_apply_bridge_fwd_mask(ocelot, false);
ocelot_port->force_forward = 0;
}
mutex_unlock(&ocelot->fwd_domain_lock);
return;
}
if (ocelot_port->force_forward) {
mutex_unlock(&ocelot->fwd_domain_lock);
return;
}
ocelot_port->force_forward = 1;
for (i = 0; i < ocelot->num_phys_ports; i++) {
if (i == port)
continue;
mask = ocelot_read_rix(ocelot, ANA_PGID_PGID, PGID_SRC + i);
mask |= BIT(port);
ocelot_write_rix(ocelot, mask, ANA_PGID_PGID, PGID_SRC + i);
}
mutex_unlock(&ocelot->fwd_domain_lock);
}
EXPORT_SYMBOL(ocelot_bridge_force_forward_port);
void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
@ -2733,7 +2763,9 @@ int ocelot_port_mqprio(struct ocelot *ocelot, int port,
if (err)
goto err_reset_tc;
ocelot_port_change_fp(ocelot, port, mqprio->preemptible_tcs);
err = ocelot_port_change_fp(ocelot, port, mqprio->preemptible_tcs);
if (err)
goto err_reset_tc;
return 0;

View File

@ -114,8 +114,6 @@ int ocelot_stats_init(struct ocelot *ocelot);
void ocelot_stats_deinit(struct ocelot *ocelot);
int ocelot_mm_init(struct ocelot *ocelot);
void ocelot_port_change_fp(struct ocelot *ocelot, int port,
unsigned long preemptible_tcs);
void ocelot_port_update_active_preemptible_tcs(struct ocelot *ocelot, int port);
extern struct notifier_block ocelot_netdevice_nb;

View File

@ -87,20 +87,30 @@ void ocelot_port_update_active_preemptible_tcs(struct ocelot *ocelot, int port)
QSYS_PREEMPTION_CFG, port);
}
void ocelot_port_change_fp(struct ocelot *ocelot, int port,
unsigned long preemptible_tcs)
int ocelot_port_change_fp(struct ocelot *ocelot, int port,
unsigned long preemptible_tcs)
{
struct ocelot_port *ocelot_port = ocelot->ports[port];
struct ocelot_mm_state *mm = &ocelot->mm[port];
lockdep_assert_held(&ocelot->fwd_domain_lock);
if (ocelot_port->cut_thru_selected_by_user & preemptible_tcs) {
dev_err(ocelot->dev,
"A traffic class cannot be preemptible and cut-through at the same time.\n");
return -EBUSY;
}
if (mm->preemptible_tcs == preemptible_tcs)
return;
return 0;
mm->preemptible_tcs = preemptible_tcs;
ocelot_port_update_active_preemptible_tcs(ocelot, port);
return 0;
}
EXPORT_SYMBOL_GPL(ocelot_port_change_fp);
static void ocelot_mm_update_port_status(struct ocelot *ocelot, int port)
{

View File

@ -54,6 +54,7 @@ struct tc_mqprio_qopt_offload;
*/
/* Reserve some destination PGIDs at the end of the range:
* PGID_FRER: Destinations for multicast traffic in 802.1CB redundant network.
* PGID_BLACKHOLE: used for not forwarding the frames
* PGID_CPU: used for whitelisting certain MAC addresses, such as the addresses
* of the switch port net devices, towards the CPU port module.
@ -63,6 +64,7 @@ struct tc_mqprio_qopt_offload;
* PGID_MCIPV6: the flooding destinations for IPv6 multicast traffic.
* PGID_BC: the flooding destinations for broadcast traffic.
*/
#define PGID_FRER 56
#define PGID_BLACKHOLE 57
#define PGID_CPU 58
#define PGID_UC 59
@ -78,7 +80,7 @@ struct tc_mqprio_qopt_offload;
#define for_each_nonreserved_multicast_dest_pgid(ocelot, pgid) \
for ((pgid) = (ocelot)->num_phys_ports + 1; \
(pgid) < PGID_BLACKHOLE; \
(pgid) < PGID_FRER; \
(pgid)++)
#define for_each_aggr_pgid(ocelot, pgid) \
@ -732,6 +734,12 @@ enum macaccess_entry_type {
ENTRYTYPE_MACv6,
};
struct ocelot_mact_entry {
u8 mac[ETH_ALEN];
u16 vid;
enum macaccess_entry_type type;
};
enum ocelot_proto {
OCELOT_PROTO_PTP_L2 = BIT(0),
OCELOT_PROTO_PTP_L4 = BIT(1),
@ -801,6 +809,10 @@ struct ocelot_port {
int bridge_num;
bool force_forward;
u8 cut_thru;
u8 cut_thru_selected_by_user;
int speed;
};
@ -895,6 +907,22 @@ struct ocelot_policer {
u32 burst; /* bytes */
};
int ocelot_mact_read(struct ocelot *ocelot, int row, int col, int *dst,
struct ocelot_mact_entry *entry);
int ocelot_mact_learn(struct ocelot *ocelot, int port,
const unsigned char mac[ETH_ALEN],
unsigned int vid, enum macaccess_entry_type type);
int ocelot_mact_forget(struct ocelot *ocelot, const unsigned char mac[ETH_ALEN],
unsigned int vid);
int ocelot_mact_lookup(struct ocelot *ocelot, int *dst_idx,
const unsigned char mac[ETH_ALEN],
unsigned int vid, enum macaccess_entry_type *type);
int ocelot_mact_learn_streamdata(struct ocelot *ocelot, int dst_idx,
const unsigned char mac[ETH_ALEN],
unsigned int vid,
enum macaccess_entry_type type,
int sfid, int ssid);
#define ocelot_bulk_read(ocelot, reg, buf, count) \
__ocelot_bulk_read_ix(ocelot, reg, 0, buf, count)
@ -1022,6 +1050,7 @@ int ocelot_get_ts_info(struct ocelot *ocelot, int port,
void ocelot_set_ageing_time(struct ocelot *ocelot, unsigned int msecs);
int ocelot_port_vlan_filtering(struct ocelot *ocelot, int port, bool enabled,
struct netlink_ext_ack *extack);
void ocelot_bridge_force_forward_port(struct ocelot *ocelot, int port, bool en);
void ocelot_bridge_stp_state_set(struct ocelot *ocelot, int port, u8 state);
u32 ocelot_get_bridge_fwd_mask(struct ocelot *ocelot, int src_port);
int ocelot_port_pre_bridge_flags(struct ocelot *ocelot, int port,
@ -1164,6 +1193,8 @@ int ocelot_port_get_mm(struct ocelot *ocelot, int port,
struct ethtool_mm_state *state);
int ocelot_port_mqprio(struct ocelot *ocelot, int port,
struct tc_mqprio_qopt_offload *mqprio);
int ocelot_port_change_fp(struct ocelot *ocelot, int port,
unsigned long preemptible_tcs);
#if IS_ENABLED(CONFIG_BRIDGE_MRP)
int ocelot_mrp_add(struct ocelot *ocelot, int port,

View File

@ -138,6 +138,9 @@
#define MACACCESS_CMD_READ 6
#define MACACCESS_CMD_WRITE 7
#define MACACCESS_ENTRY_TYPE_NORMAL 0
#define MACACCESS_ENTRY_TYPE_LOCKED 1
#define ANA_TABLES_VLANACCESS_VLAN_PORT_MASK(x) (((x) << 2) & GENMASK(13, 2))
#define ANA_TABLES_VLANACCESS_VLAN_PORT_MASK_M GENMASK(13, 2)
#define ANA_TABLES_VLANACCESS_VLAN_PORT_MASK_X(x) (((x) & GENMASK(13, 2)) >> 2)
@ -271,6 +274,9 @@
#define ANA_SG_GCL_GS_CONFIG_IPS(x) ((x) & GENMASK(3, 0))
#define ANA_SG_GCL_GS_CONFIG_IPS_M GENMASK(3, 0)
#define ANA_SG_GCL_GS_CONFIG_IPV_VALID BIT(3)
#define ANA_SG_GCL_GS_CONFIG_IPV(x) ((x) & GENMASK(2, 0))
#define ANA_SG_GCL_GS_CONFIG_IPV_M GENMASK(2, 0)
#define ANA_SG_GCL_GS_CONFIG_GATE_STATE BIT(4)
#define ANA_SG_GCL_TI_CONFIG_RSZ 0x4
@ -281,6 +287,10 @@
#define ANA_SG_STATUS_REG_3_IPS(x) (((x) << 20) & GENMASK(23, 20))
#define ANA_SG_STATUS_REG_3_IPS_M GENMASK(23, 20)
#define ANA_SG_STATUS_REG_3_IPS_X(x) (((x) & GENMASK(23, 20)) >> 20)
#define ANA_SG_STATUS_REG_3_IPV_VALID BIT(23)
#define ANA_SG_STATUS_REG_3_IPV(x) (((x) << 20) & GENMASK(22, 20))
#define ANA_SG_STATUS_REG_3_IPV_M GENMASK(22, 20)
#define ANA_SG_STATUS_REG_3_IPV_X(x) (((x) & GENMASK(22, 20)) >> 20)
#define ANA_SG_STATUS_REG_3_CONFIG_PENDING BIT(24)
#define ANA_PORT_VLAN_CFG_GSZ 0x100