mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 01:08:13 -04:00
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:
14
include/linux/dsa/tag_netc.h
Normal file
14
include/linux/dsa/tag_netc.h
Normal 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
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
214
net/dsa/tag_netc.c
Normal 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);
|
||||
Reference in New Issue
Block a user