summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/freescale/dpaa2/dpaa2-switch.h
blob: 0002dca4d4177128eba7363bdf842d07e34773a5 (plain)
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
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
/* SPDX-License-Identifier: GPL-2.0 */
/*
 * DPAA2 Ethernet Switch declarations
 *
 * Copyright 2014-2016 Freescale Semiconductor Inc.
 * Copyright 2017-2021 NXP
 *
 */

#ifndef __ETHSW_H
#define __ETHSW_H

#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/rtnetlink.h>
#include <linux/if_vlan.h>
#include <uapi/linux/if_bridge.h>
#include <net/switchdev.h>
#include <linux/if_bridge.h>
#include <linux/fsl/mc.h>
#include <net/pkt_cls.h>
#include <soc/fsl/dpaa2-io.h>

#include "dpaa2-mac.h"
#include "dpsw.h"

/* Number of IRQs supported */
#define DPSW_IRQ_NUM	2

/* Port is member of VLAN */
#define ETHSW_VLAN_MEMBER	1
/* VLAN to be treated as untagged on egress */
#define ETHSW_VLAN_UNTAGGED	2
/* Untagged frames will be assigned to this VLAN */
#define ETHSW_VLAN_PVID		4
/* VLAN configured on the switch */
#define ETHSW_VLAN_GLOBAL	8

/* Maximum Frame Length supported by HW (currently 10k) */
#define DPAA2_MFL		(10 * 1024)
#define ETHSW_MAX_FRAME_LENGTH	(DPAA2_MFL - VLAN_ETH_HLEN - ETH_FCS_LEN)
#define ETHSW_L2_MAX_FRM(mtu)	((mtu) + VLAN_ETH_HLEN + ETH_FCS_LEN)

#define ETHSW_FEATURE_MAC_ADDR	BIT(0)

/* Number of receive queues (one RX and one TX_CONF) */
#define DPAA2_SWITCH_RX_NUM_FQS	2

/* Hardware requires alignment for ingress/egress buffer addresses */
#define DPAA2_SWITCH_RX_BUF_RAW_SIZE	PAGE_SIZE
#define DPAA2_SWITCH_RX_BUF_TAILROOM \
	SKB_DATA_ALIGN(sizeof(struct skb_shared_info))
#define DPAA2_SWITCH_RX_BUF_SIZE \
	(DPAA2_SWITCH_RX_BUF_RAW_SIZE - DPAA2_SWITCH_RX_BUF_TAILROOM)

#define DPAA2_SWITCH_STORE_SIZE 16

/* Buffer management */
#define BUFS_PER_CMD			7
#define DPAA2_ETHSW_NUM_BUFS		(1024 * BUFS_PER_CMD)
#define DPAA2_ETHSW_REFILL_THRESH	(DPAA2_ETHSW_NUM_BUFS * 5 / 6)

/* Number of times to retry DPIO portal operations while waiting
 * for portal to finish executing current command and become
 * available. We want to avoid being stuck in a while loop in case
 * hardware becomes unresponsive, but not give up too easily if
 * the portal really is busy for valid reasons
 */
#define DPAA2_SWITCH_SWP_BUSY_RETRIES		1000

/* Hardware annotation buffer size */
#define DPAA2_SWITCH_HWA_SIZE			64
/* Software annotation buffer size */
#define DPAA2_SWITCH_SWA_SIZE			64

#define DPAA2_SWITCH_TX_BUF_ALIGN		64

#define DPAA2_SWITCH_TX_DATA_OFFSET \
	(DPAA2_SWITCH_HWA_SIZE + DPAA2_SWITCH_SWA_SIZE)

#define DPAA2_SWITCH_NEEDED_HEADROOM \
	(DPAA2_SWITCH_TX_DATA_OFFSET + DPAA2_SWITCH_TX_BUF_ALIGN)

#define DPAA2_ETHSW_PORT_MAX_ACL_ENTRIES	16
#define DPAA2_ETHSW_PORT_DEFAULT_TRAPS		1

#define DPAA2_ETHSW_PORT_ACL_CMD_BUF_SIZE	256

extern const struct ethtool_ops dpaa2_switch_port_ethtool_ops;

struct ethsw_core;

struct dpaa2_switch_fq {
	struct ethsw_core *ethsw;
	enum dpsw_queue_type type;
	struct dpaa2_io_store *store;
	struct dpaa2_io_notification_ctx nctx;
	struct napi_struct napi;
	u32 fqid;
};

struct dpaa2_switch_fdb {
	struct net_device	*bridge_dev;
	u16			fdb_id;
	bool			in_use;
};

struct dpaa2_switch_acl_entry {
	struct list_head	list;
	u16			prio;
	unsigned long		cookie;

	struct dpsw_acl_entry_cfg cfg;
	struct dpsw_acl_key	key;
};

struct dpaa2_switch_mirror_entry {
	struct list_head	list;
	struct dpsw_reflection_cfg cfg;
	unsigned long		cookie;
	u16 if_id;
};

struct dpaa2_switch_filter_block {
	struct ethsw_core	*ethsw;
	u64			ports;
	bool			in_use;

	struct list_head	acl_entries;
	u16			acl_id;
	u8			num_acl_rules;

	struct list_head	mirror_entries;
};

static inline bool
dpaa2_switch_acl_tbl_is_full(struct dpaa2_switch_filter_block *filter_block)
{
	if ((filter_block->num_acl_rules + DPAA2_ETHSW_PORT_DEFAULT_TRAPS) >=
	    DPAA2_ETHSW_PORT_MAX_ACL_ENTRIES)
		return true;
	return false;
}

/* Per port private data */
struct ethsw_port_priv {
	struct net_device	*netdev;
	u16			idx;
	struct ethsw_core	*ethsw_data;
	u8			link_state;
	u8			stp_state;

	u8			vlans[VLAN_VID_MASK + 1];
	u16			pvid;
	u16			tx_qdid;

	struct dpaa2_switch_fdb	*fdb;
	bool			bcast_flood;
	bool			ucast_flood;
	bool			learn_ena;

	struct dpaa2_switch_filter_block *filter_block;
	struct dpaa2_mac	*mac;
};

/* Switch data */
struct ethsw_core {
	struct device			*dev;
	struct fsl_mc_io		*mc_io;
	u16				dpsw_handle;
	struct dpsw_attr		sw_attr;
	u16				major, minor;
	unsigned long			features;
	int				dev_id;
	struct ethsw_port_priv		**ports;
	struct iommu_domain		*iommu_domain;

	u8				vlans[VLAN_VID_MASK + 1];

	struct workqueue_struct		*workqueue;

	struct dpaa2_switch_fq		fq[DPAA2_SWITCH_RX_NUM_FQS];
	struct fsl_mc_device		*dpbp_dev;
	int				buf_count;
	u16				bpid;
	int				napi_users;

	struct dpaa2_switch_fdb		*fdbs;
	struct dpaa2_switch_filter_block *filter_blocks;
	u16				mirror_port;
};

static inline int dpaa2_switch_get_index(struct ethsw_core *ethsw,
					 struct net_device *netdev)
{
	int i;

	for (i = 0; i < ethsw->sw_attr.num_ifs; i++)
		if (ethsw->ports[i]->netdev == netdev)
			return ethsw->ports[i]->idx;

	return -EINVAL;
}

static inline bool dpaa2_switch_supports_cpu_traffic(struct ethsw_core *ethsw)
{
	if (ethsw->sw_attr.options & DPSW_OPT_CTRL_IF_DIS) {
		dev_err(ethsw->dev, "Control Interface is disabled, cannot probe\n");
		return false;
	}

	if (ethsw->sw_attr.flooding_cfg != DPSW_FLOODING_PER_FDB) {
		dev_err(ethsw->dev, "Flooding domain is not per FDB, cannot probe\n");
		return false;
	}

	if (ethsw->sw_attr.broadcast_cfg != DPSW_BROADCAST_PER_FDB) {
		dev_err(ethsw->dev, "Broadcast domain is not per FDB, cannot probe\n");
		return false;
	}

	if (ethsw->sw_attr.max_fdbs < ethsw->sw_attr.num_ifs) {
		dev_err(ethsw->dev, "The number of FDBs is lower than the number of ports, cannot probe\n");
		return false;
	}

	return true;
}

static inline bool
dpaa2_switch_port_is_type_phy(struct ethsw_port_priv *port_priv)
{
	if (port_priv->mac &&
	    (port_priv->mac->attr.link_type == DPMAC_LINK_TYPE_PHY ||
	     port_priv->mac->attr.link_type == DPMAC_LINK_TYPE_BACKPLANE))
		return true;

	return false;
}

static inline bool dpaa2_switch_port_has_mac(struct ethsw_port_priv *port_priv)
{
	return port_priv->mac ? true : false;
}

bool dpaa2_switch_port_dev_check(const struct net_device *netdev);

int dpaa2_switch_port_vlans_add(struct net_device *netdev,
				const struct switchdev_obj_port_vlan *vlan);

int dpaa2_switch_port_vlans_del(struct net_device *netdev,
				const struct switchdev_obj_port_vlan *vlan);

typedef int dpaa2_switch_fdb_cb_t(struct ethsw_port_priv *port_priv,
				  struct fdb_dump_entry *fdb_entry,
				  void *data);

/* TC offload */

int dpaa2_switch_cls_flower_replace(struct dpaa2_switch_filter_block *block,
				    struct flow_cls_offload *cls);

int dpaa2_switch_cls_flower_destroy(struct dpaa2_switch_filter_block *block,
				    struct flow_cls_offload *cls);

int dpaa2_switch_cls_matchall_replace(struct dpaa2_switch_filter_block *block,
				      struct tc_cls_matchall_offload *cls);

int dpaa2_switch_cls_matchall_destroy(struct dpaa2_switch_filter_block *block,
				      struct tc_cls_matchall_offload *cls);

int dpaa2_switch_acl_entry_add(struct dpaa2_switch_filter_block *block,
			       struct dpaa2_switch_acl_entry *entry);

int dpaa2_switch_block_offload_mirror(struct dpaa2_switch_filter_block *block,
				      struct ethsw_port_priv *port_priv);

int dpaa2_switch_block_unoffload_mirror(struct dpaa2_switch_filter_block *block,
					struct ethsw_port_priv *port_priv);
#endif	/* __ETHSW_H */