summaryrefslogtreecommitdiff
path: root/net/dsa/tag_netc.c
blob: df72a61796ad5f41bca04cf765218b06a0094fe0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
// 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)))
		goto err_free_skb;

	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));
		goto err_free_skb;
	}

	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");
		goto err_free_skb;
	}

	port = FIELD_GET(NETC_TAG_PORT, tag_cmn->switch_port);
	skb->dev = dsa_conduit_find_user(ndev, sw_id, port);
	if (!skb->dev)
		goto err_free_skb;

	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)
			goto err_free_skb;
	} else {
		dev_warn_ratelimited(&ndev->dev,
				     "Unexpected  tag type %d\n", type);
		goto err_free_skb;
	}

	/* 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;

err_free_skb:
	kfree_skb(skb);
	return NULL;
}

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);