net: dsa: add NETC switch tag support

The NXP NETC switch tag is a proprietary header added to frames after the
source MAC address. The switch tag has 3 types, and each type has 1 ~ 4
subtypes, the details are as follows.

Forward NXP switch tag (Type=0): Represents forwarded frames.
  - SubType = 0 - Normal frame processing.

To_Port NXP switch tag (Type=1): Represents frames that are to be sent
to a specific switch port.
  - SubType = 0. No request to perform timestamping.
  - SubType = 1. Request to perform one-step timestamping.
  - SubType = 2. Request to perform two-step timestamping.
  - SubType = 3. Request to perform both one-step timestamping and
    two-step timestamping.

To_Host NXP switch tag (Type=2): Represents frames redirected or copied
to the switch management port.
  - SubType = 0. Received frames redirected or copied to the switch
    management port.
  - SubType = 1. Received frames redirected or copied to the switch
    management port with captured timestamp at the switch port where
    the frame was received.
  - SubType = 2. Transmit timestamp response (two-step timestamping).

In addition, the length of different type switch tag is different, the
minimum length is 6 bytes, the maximum length is 14 bytes. Currently,
Forward tag, SubType 0 of To_Port tag and Subtype 0 of To_Host tag are
supported. More tags will be supported in the future.

Signed-off-by: Wei Fang <wei.fang@nxp.com>
Link: https://patch.msgid.link/20260518082506.1318236-10-wei.fang@nxp.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
This commit is contained in:
Wei Fang
2026-05-18 16:25:00 +08:00
committed by Paolo Abeni
parent c5aed83ddc
commit 0850005c26
6 changed files with 242 additions and 0 deletions

View File

@@ -0,0 +1,14 @@
/* SPDX-License-Identifier: GPL-2.0
*
* Copyright 2025-2026 NXP
*/
#ifndef __NET_DSA_TAG_NETC_H
#define __NET_DSA_TAG_NETC_H
#include <linux/skbuff.h>
#include <net/dsa.h>
#define NETC_TAG_MAX_LEN 14
#endif

View File

@@ -58,6 +58,7 @@ struct tc_action;
#define DSA_TAG_PROTO_YT921X_VALUE 30
#define DSA_TAG_PROTO_MXL_GSW1XX_VALUE 31
#define DSA_TAG_PROTO_MXL862_VALUE 32
#define DSA_TAG_PROTO_NETC_VALUE 33
enum dsa_tag_protocol {
DSA_TAG_PROTO_NONE = DSA_TAG_PROTO_NONE_VALUE,
@@ -93,6 +94,7 @@ enum dsa_tag_protocol {
DSA_TAG_PROTO_YT921X = DSA_TAG_PROTO_YT921X_VALUE,
DSA_TAG_PROTO_MXL_GSW1XX = DSA_TAG_PROTO_MXL_GSW1XX_VALUE,
DSA_TAG_PROTO_MXL862 = DSA_TAG_PROTO_MXL862_VALUE,
DSA_TAG_PROTO_NETC = DSA_TAG_PROTO_NETC_VALUE,
};
struct dsa_switch;

View File

@@ -123,6 +123,7 @@
#define ETH_P_DSA_A5PSW 0xE001 /* A5PSW Tag Value [ NOT AN OFFICIALLY REGISTERED ID ] */
#define ETH_P_IFE 0xED3E /* ForCES inter-FE LFB type */
#define ETH_P_AF_IUCV 0xFBFB /* IBM af_iucv [ NOT AN OFFICIALLY REGISTERED ID ] */
#define ETH_P_NXP_NETC 0xFD3A /* NXP NETC DSA [ NOT AN OFFICIALLY REGISTERED ID ] */
#define ETH_P_802_3_MIN 0x0600 /* If the value in the ethernet type is more than this value
* then the frame is Ethernet II. Else it is 802.3 */

View File

@@ -125,6 +125,16 @@ config NET_DSA_TAG_KSZ
Say Y if you want to enable support for tagging frames for the
Microchip 8795/937x/9477/9893 families of switches.
config NET_DSA_TAG_NETC
tristate "Tag driver for NXP NETC switches"
help
Say Y or M if you want to enable support for the NXP Switch Tag (NST),
as implemented by NXP NETC switches having version 4.3 or later. The
switch tag is a proprietary header added to frames after the source
MAC address, it has 3 types and each type has different subtypes, so
its length depends on the type and subtype of the tag, the maximum
length is 14 bytes.
config NET_DSA_TAG_OCELOT
tristate "Tag driver for Ocelot family of switches, using NPI port"
select PACKING

View File

@@ -30,6 +30,7 @@ obj-$(CONFIG_NET_DSA_TAG_LAN9303) += tag_lan9303.o
obj-$(CONFIG_NET_DSA_TAG_MTK) += tag_mtk.o
obj-$(CONFIG_NET_DSA_TAG_MXL_862XX) += tag_mxl862xx.o
obj-$(CONFIG_NET_DSA_TAG_MXL_GSW1XX) += tag_mxl-gsw1xx.o
obj-$(CONFIG_NET_DSA_TAG_NETC) += tag_netc.o
obj-$(CONFIG_NET_DSA_TAG_NONE) += tag_none.o
obj-$(CONFIG_NET_DSA_TAG_OCELOT) += tag_ocelot.o
obj-$(CONFIG_NET_DSA_TAG_OCELOT_8021Q) += tag_ocelot_8021q.o

214
net/dsa/tag_netc.c Normal file
View File

@@ -0,0 +1,214 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright 2025-2026 NXP
*/
#include <linux/dsa/tag_netc.h>
#include "tag.h"
#define NETC_NAME "nxp_netc"
/* Forward NXP switch tag */
#define NETC_TAG_FORWARD 0
/* To_Port NXP switch tag */
#define NETC_TAG_TO_PORT 1
/* SubType0: No request to perform timestamping */
#define NETC_TAG_TP_SUBTYPE0 0
/* To_Host NXP switch tag */
#define NETC_TAG_TO_HOST 2
/* SubType0: frames redirected or copied to CPU port */
#define NETC_TAG_TH_SUBTYPE0 0
/* SubType1: frames redirected or copied to CPU port with timestamp */
#define NETC_TAG_TH_SUBTYPE1 1
/* SubType2: Transmit timestamp response (two-step timestamping) */
#define NETC_TAG_TH_SUBTYPE2 2
/* NETC switch tag lengths */
#define NETC_TAG_FORWARD_LEN 6
#define NETC_TAG_TP_SUBTYPE0_LEN 6
#define NETC_TAG_TH_SUBTYPE0_LEN 6
#define NETC_TAG_TH_SUBTYPE1_LEN 14
#define NETC_TAG_TH_SUBTYPE2_LEN 14
#define NETC_TAG_CMN_LEN 5
#define NETC_TAG_SUBTYPE GENMASK(3, 0)
#define NETC_TAG_TYPE GENMASK(7, 4)
#define NETC_TAG_QV BIT(0)
#define NETC_TAG_IPV GENMASK(4, 2)
#define NETC_TAG_SWITCH GENMASK(2, 0)
#define NETC_TAG_PORT GENMASK(7, 3)
struct netc_tag_cmn {
__be16 tpid;
u8 type;
u8 qos;
u8 switch_port;
} __packed;
static void netc_fill_common_tag(struct netc_tag_cmn *tag, u8 type,
u8 subtype, u8 sw_id, u8 port, u8 ipv)
{
tag->tpid = htons(ETH_P_NXP_NETC);
tag->type = FIELD_PREP(NETC_TAG_TYPE, type) |
FIELD_PREP(NETC_TAG_SUBTYPE, subtype);
tag->qos = NETC_TAG_QV | FIELD_PREP(NETC_TAG_IPV, ipv);
tag->switch_port = FIELD_PREP(NETC_TAG_SWITCH, sw_id) |
FIELD_PREP(NETC_TAG_PORT, port);
}
static void *netc_fill_common_tp_tag(struct sk_buff *skb,
struct net_device *ndev,
u8 subtype, int tag_len)
{
struct dsa_port *dp = dsa_user_to_port(ndev);
u16 queue = skb_get_queue_mapping(skb);
s8 ipv = netdev_txq_to_tc(ndev, queue);
void *tag;
if (unlikely(ipv < 0))
ipv = 0;
skb_push(skb, tag_len);
dsa_alloc_etype_header(skb, tag_len);
tag = dsa_etype_header_pos_tx(skb);
memset(tag + NETC_TAG_CMN_LEN, 0, tag_len - NETC_TAG_CMN_LEN);
/* As 'dsa,member' is a required property for NETC switch, the member
* is used to specify the switch ID (thus the hardware switch ID and
* the software switch ID are consistent), its range is 1 ~ 7. The
* NETC switch driver will check this value, and if it is invalid,
* the switch driver will fail the probe.
* In addition, according to the nxp,netc-switch.yaml doc, the port
* index will not be greater than 0xf.
*/
netc_fill_common_tag(tag, NETC_TAG_TO_PORT, subtype,
dp->ds->index, dp->index, ipv);
return tag;
}
static void netc_fill_tp_tag_subtype0(struct sk_buff *skb,
struct net_device *ndev)
{
netc_fill_common_tp_tag(skb, ndev, NETC_TAG_TP_SUBTYPE0,
NETC_TAG_TP_SUBTYPE0_LEN);
}
/* Currently only support To_Port tag, subtype 0 */
static struct sk_buff *netc_xmit(struct sk_buff *skb,
struct net_device *ndev)
{
netc_fill_tp_tag_subtype0(skb, ndev);
return skb;
}
static int netc_get_rx_tag_len(int type, int subtype)
{
/* Only NETC_TAG_TO_HOST and NETC_TAG_FORWARD are expected in RX,
* NETC_TAG_TO_PORT is a TX switch tag that does not exist in RX.
*/
if (type == NETC_TAG_TO_HOST) {
if (subtype == NETC_TAG_TH_SUBTYPE1)
return NETC_TAG_TH_SUBTYPE1_LEN;
else if (subtype == NETC_TAG_TH_SUBTYPE2)
return NETC_TAG_TH_SUBTYPE2_LEN;
else
return NETC_TAG_TH_SUBTYPE0_LEN;
}
return NETC_TAG_FORWARD_LEN;
}
static struct sk_buff *netc_rcv(struct sk_buff *skb,
struct net_device *ndev)
{
struct netc_tag_cmn *tag_cmn;
int tag_len, sw_id, port;
int type, subtype;
if (unlikely(!pskb_may_pull(skb, NETC_TAG_MAX_LEN)))
return NULL;
tag_cmn = dsa_etype_header_pos_rx(skb);
if (ntohs(tag_cmn->tpid) != ETH_P_NXP_NETC) {
dev_warn_ratelimited(&ndev->dev, "Unknown TPID 0x%04x\n",
ntohs(tag_cmn->tpid));
return NULL;
}
if (tag_cmn->qos & NETC_TAG_QV)
skb->priority = FIELD_GET(NETC_TAG_IPV, tag_cmn->qos);
sw_id = FIELD_GET(NETC_TAG_SWITCH, tag_cmn->switch_port);
/* ENETC VEPA switch ID (0) is not supported yet */
if (!sw_id) {
dev_warn_ratelimited(&ndev->dev,
"VEPA switch ID is not supported yet\n");
return NULL;
}
port = FIELD_GET(NETC_TAG_PORT, tag_cmn->switch_port);
skb->dev = dsa_conduit_find_user(ndev, sw_id, port);
if (!skb->dev)
return NULL;
type = FIELD_GET(NETC_TAG_TYPE, tag_cmn->type);
subtype = FIELD_GET(NETC_TAG_SUBTYPE, tag_cmn->type);
if (type == NETC_TAG_FORWARD) {
dsa_default_offload_fwd_mark(skb);
} else if (type == NETC_TAG_TO_HOST) {
/* Currently only subtype0 supported */
if (subtype != NETC_TAG_TH_SUBTYPE0)
return NULL;
} else {
dev_warn_ratelimited(&ndev->dev,
"Unexpected tag type %d\n", type);
return NULL;
}
/* Remove Switch tag from the frame */
tag_len = netc_get_rx_tag_len(type, subtype);
skb_pull_rcsum(skb, tag_len);
dsa_strip_etype_header(skb, tag_len);
return skb;
}
static void netc_flow_dissect(const struct sk_buff *skb, __be16 *proto,
int *offset)
{
struct netc_tag_cmn *tag_cmn = (struct netc_tag_cmn *)(skb->data - 2);
int subtype = FIELD_GET(NETC_TAG_SUBTYPE, tag_cmn->type);
int type = FIELD_GET(NETC_TAG_TYPE, tag_cmn->type);
int tag_len = netc_get_rx_tag_len(type, subtype);
/* The RX minimum frame length of the NETC switch port is 64 bytes,
* and the frame is received by the ENETC driver. From the hardware
* perspective, the receive buffer of RX BD is at least 128 bytes,
* so the switch tag header is guaranteed to be in the linear region
* of the skb.
*/
*offset = tag_len;
*proto = ((__be16 *)skb->data)[(tag_len / 2) - 1];
}
static const struct dsa_device_ops netc_netdev_ops = {
.name = NETC_NAME,
.proto = DSA_TAG_PROTO_NETC,
.xmit = netc_xmit,
.rcv = netc_rcv,
.needed_headroom = NETC_TAG_MAX_LEN,
.flow_dissect = netc_flow_dissect,
};
MODULE_DESCRIPTION("DSA tag driver for NXP NETC switch family");
MODULE_LICENSE("GPL");
MODULE_ALIAS_DSA_TAG_DRIVER(DSA_TAG_PROTO_NETC, NETC_NAME);
module_dsa_tag_driver(netc_netdev_ops);