xref: /OK3568_Linux_fs/kernel/drivers/net/dsa/sja1105/sja1105_vl.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /* Copyright 2020, NXP Semiconductors
3*4882a593Smuzhiyun  */
4*4882a593Smuzhiyun #include <net/tc_act/tc_gate.h>
5*4882a593Smuzhiyun #include <linux/dsa/8021q.h>
6*4882a593Smuzhiyun #include "sja1105_vl.h"
7*4882a593Smuzhiyun 
8*4882a593Smuzhiyun #define SJA1105_SIZE_VL_STATUS			8
9*4882a593Smuzhiyun 
10*4882a593Smuzhiyun /* Insert into the global gate list, sorted by gate action time. */
sja1105_insert_gate_entry(struct sja1105_gating_config * gating_cfg,struct sja1105_rule * rule,u8 gate_state,s64 entry_time,struct netlink_ext_ack * extack)11*4882a593Smuzhiyun static int sja1105_insert_gate_entry(struct sja1105_gating_config *gating_cfg,
12*4882a593Smuzhiyun 				     struct sja1105_rule *rule,
13*4882a593Smuzhiyun 				     u8 gate_state, s64 entry_time,
14*4882a593Smuzhiyun 				     struct netlink_ext_ack *extack)
15*4882a593Smuzhiyun {
16*4882a593Smuzhiyun 	struct sja1105_gate_entry *e;
17*4882a593Smuzhiyun 	int rc;
18*4882a593Smuzhiyun 
19*4882a593Smuzhiyun 	e = kzalloc(sizeof(*e), GFP_KERNEL);
20*4882a593Smuzhiyun 	if (!e)
21*4882a593Smuzhiyun 		return -ENOMEM;
22*4882a593Smuzhiyun 
23*4882a593Smuzhiyun 	e->rule = rule;
24*4882a593Smuzhiyun 	e->gate_state = gate_state;
25*4882a593Smuzhiyun 	e->interval = entry_time;
26*4882a593Smuzhiyun 
27*4882a593Smuzhiyun 	if (list_empty(&gating_cfg->entries)) {
28*4882a593Smuzhiyun 		list_add(&e->list, &gating_cfg->entries);
29*4882a593Smuzhiyun 	} else {
30*4882a593Smuzhiyun 		struct sja1105_gate_entry *p;
31*4882a593Smuzhiyun 
32*4882a593Smuzhiyun 		list_for_each_entry(p, &gating_cfg->entries, list) {
33*4882a593Smuzhiyun 			if (p->interval == e->interval) {
34*4882a593Smuzhiyun 				NL_SET_ERR_MSG_MOD(extack,
35*4882a593Smuzhiyun 						   "Gate conflict");
36*4882a593Smuzhiyun 				rc = -EBUSY;
37*4882a593Smuzhiyun 				goto err;
38*4882a593Smuzhiyun 			}
39*4882a593Smuzhiyun 
40*4882a593Smuzhiyun 			if (e->interval < p->interval)
41*4882a593Smuzhiyun 				break;
42*4882a593Smuzhiyun 		}
43*4882a593Smuzhiyun 		list_add(&e->list, p->list.prev);
44*4882a593Smuzhiyun 	}
45*4882a593Smuzhiyun 
46*4882a593Smuzhiyun 	gating_cfg->num_entries++;
47*4882a593Smuzhiyun 
48*4882a593Smuzhiyun 	return 0;
49*4882a593Smuzhiyun err:
50*4882a593Smuzhiyun 	kfree(e);
51*4882a593Smuzhiyun 	return rc;
52*4882a593Smuzhiyun }
53*4882a593Smuzhiyun 
54*4882a593Smuzhiyun /* The gate entries contain absolute times in their e->interval field. Convert
55*4882a593Smuzhiyun  * that to proper intervals (i.e. "0, 5, 10, 15" to "5, 5, 5, 5").
56*4882a593Smuzhiyun  */
57*4882a593Smuzhiyun static void
sja1105_gating_cfg_time_to_interval(struct sja1105_gating_config * gating_cfg,u64 cycle_time)58*4882a593Smuzhiyun sja1105_gating_cfg_time_to_interval(struct sja1105_gating_config *gating_cfg,
59*4882a593Smuzhiyun 				    u64 cycle_time)
60*4882a593Smuzhiyun {
61*4882a593Smuzhiyun 	struct sja1105_gate_entry *last_e;
62*4882a593Smuzhiyun 	struct sja1105_gate_entry *e;
63*4882a593Smuzhiyun 	struct list_head *prev;
64*4882a593Smuzhiyun 
65*4882a593Smuzhiyun 	list_for_each_entry(e, &gating_cfg->entries, list) {
66*4882a593Smuzhiyun 		struct sja1105_gate_entry *p;
67*4882a593Smuzhiyun 
68*4882a593Smuzhiyun 		prev = e->list.prev;
69*4882a593Smuzhiyun 
70*4882a593Smuzhiyun 		if (prev == &gating_cfg->entries)
71*4882a593Smuzhiyun 			continue;
72*4882a593Smuzhiyun 
73*4882a593Smuzhiyun 		p = list_entry(prev, struct sja1105_gate_entry, list);
74*4882a593Smuzhiyun 		p->interval = e->interval - p->interval;
75*4882a593Smuzhiyun 	}
76*4882a593Smuzhiyun 	last_e = list_last_entry(&gating_cfg->entries,
77*4882a593Smuzhiyun 				 struct sja1105_gate_entry, list);
78*4882a593Smuzhiyun 	last_e->interval = cycle_time - last_e->interval;
79*4882a593Smuzhiyun }
80*4882a593Smuzhiyun 
sja1105_free_gating_config(struct sja1105_gating_config * gating_cfg)81*4882a593Smuzhiyun static void sja1105_free_gating_config(struct sja1105_gating_config *gating_cfg)
82*4882a593Smuzhiyun {
83*4882a593Smuzhiyun 	struct sja1105_gate_entry *e, *n;
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun 	list_for_each_entry_safe(e, n, &gating_cfg->entries, list) {
86*4882a593Smuzhiyun 		list_del(&e->list);
87*4882a593Smuzhiyun 		kfree(e);
88*4882a593Smuzhiyun 	}
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun 
sja1105_compose_gating_subschedule(struct sja1105_private * priv,struct netlink_ext_ack * extack)91*4882a593Smuzhiyun static int sja1105_compose_gating_subschedule(struct sja1105_private *priv,
92*4882a593Smuzhiyun 					      struct netlink_ext_ack *extack)
93*4882a593Smuzhiyun {
94*4882a593Smuzhiyun 	struct sja1105_gating_config *gating_cfg = &priv->tas_data.gating_cfg;
95*4882a593Smuzhiyun 	struct sja1105_rule *rule;
96*4882a593Smuzhiyun 	s64 max_cycle_time = 0;
97*4882a593Smuzhiyun 	s64 its_base_time = 0;
98*4882a593Smuzhiyun 	int i, rc = 0;
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun 	sja1105_free_gating_config(gating_cfg);
101*4882a593Smuzhiyun 
102*4882a593Smuzhiyun 	list_for_each_entry(rule, &priv->flow_block.rules, list) {
103*4882a593Smuzhiyun 		if (rule->type != SJA1105_RULE_VL)
104*4882a593Smuzhiyun 			continue;
105*4882a593Smuzhiyun 		if (rule->vl.type != SJA1105_VL_TIME_TRIGGERED)
106*4882a593Smuzhiyun 			continue;
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun 		if (max_cycle_time < rule->vl.cycle_time) {
109*4882a593Smuzhiyun 			max_cycle_time = rule->vl.cycle_time;
110*4882a593Smuzhiyun 			its_base_time = rule->vl.base_time;
111*4882a593Smuzhiyun 		}
112*4882a593Smuzhiyun 	}
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 	if (!max_cycle_time)
115*4882a593Smuzhiyun 		return 0;
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun 	dev_dbg(priv->ds->dev, "max_cycle_time %lld its_base_time %lld\n",
118*4882a593Smuzhiyun 		max_cycle_time, its_base_time);
119*4882a593Smuzhiyun 
120*4882a593Smuzhiyun 	gating_cfg->base_time = its_base_time;
121*4882a593Smuzhiyun 	gating_cfg->cycle_time = max_cycle_time;
122*4882a593Smuzhiyun 	gating_cfg->num_entries = 0;
123*4882a593Smuzhiyun 
124*4882a593Smuzhiyun 	list_for_each_entry(rule, &priv->flow_block.rules, list) {
125*4882a593Smuzhiyun 		s64 time;
126*4882a593Smuzhiyun 		s64 rbt;
127*4882a593Smuzhiyun 
128*4882a593Smuzhiyun 		if (rule->type != SJA1105_RULE_VL)
129*4882a593Smuzhiyun 			continue;
130*4882a593Smuzhiyun 		if (rule->vl.type != SJA1105_VL_TIME_TRIGGERED)
131*4882a593Smuzhiyun 			continue;
132*4882a593Smuzhiyun 
133*4882a593Smuzhiyun 		/* Calculate the difference between this gating schedule's
134*4882a593Smuzhiyun 		 * base time, and the base time of the gating schedule with the
135*4882a593Smuzhiyun 		 * longest cycle time. We call it the relative base time (rbt).
136*4882a593Smuzhiyun 		 */
137*4882a593Smuzhiyun 		rbt = future_base_time(rule->vl.base_time, rule->vl.cycle_time,
138*4882a593Smuzhiyun 				       its_base_time);
139*4882a593Smuzhiyun 		rbt -= its_base_time;
140*4882a593Smuzhiyun 
141*4882a593Smuzhiyun 		time = rbt;
142*4882a593Smuzhiyun 
143*4882a593Smuzhiyun 		for (i = 0; i < rule->vl.num_entries; i++) {
144*4882a593Smuzhiyun 			u8 gate_state = rule->vl.entries[i].gate_state;
145*4882a593Smuzhiyun 			s64 entry_time = time;
146*4882a593Smuzhiyun 
147*4882a593Smuzhiyun 			while (entry_time < max_cycle_time) {
148*4882a593Smuzhiyun 				rc = sja1105_insert_gate_entry(gating_cfg, rule,
149*4882a593Smuzhiyun 							       gate_state,
150*4882a593Smuzhiyun 							       entry_time,
151*4882a593Smuzhiyun 							       extack);
152*4882a593Smuzhiyun 				if (rc)
153*4882a593Smuzhiyun 					goto err;
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun 				entry_time += rule->vl.cycle_time;
156*4882a593Smuzhiyun 			}
157*4882a593Smuzhiyun 			time += rule->vl.entries[i].interval;
158*4882a593Smuzhiyun 		}
159*4882a593Smuzhiyun 	}
160*4882a593Smuzhiyun 
161*4882a593Smuzhiyun 	sja1105_gating_cfg_time_to_interval(gating_cfg, max_cycle_time);
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun 	return 0;
164*4882a593Smuzhiyun err:
165*4882a593Smuzhiyun 	sja1105_free_gating_config(gating_cfg);
166*4882a593Smuzhiyun 	return rc;
167*4882a593Smuzhiyun }
168*4882a593Smuzhiyun 
169*4882a593Smuzhiyun /* The switch flow classification core implements TTEthernet, which 'thinks' in
170*4882a593Smuzhiyun  * terms of Virtual Links (VL), a concept borrowed from ARINC 664 part 7.
171*4882a593Smuzhiyun  * However it also has one other operating mode (VLLUPFORMAT=0) where it acts
172*4882a593Smuzhiyun  * somewhat closer to a pre-standard implementation of IEEE 802.1Qci
173*4882a593Smuzhiyun  * (Per-Stream Filtering and Policing), which is what the driver is going to be
174*4882a593Smuzhiyun  * implementing.
175*4882a593Smuzhiyun  *
176*4882a593Smuzhiyun  *                                 VL Lookup
177*4882a593Smuzhiyun  *        Key = {DMAC && VLANID   +---------+  Key = { (DMAC[47:16] & VLMASK ==
178*4882a593Smuzhiyun  *               && VLAN PCP      |         |                         VLMARKER)
179*4882a593Smuzhiyun  *               && INGRESS PORT} +---------+                      (both fixed)
180*4882a593Smuzhiyun  *            (exact match,            |             && DMAC[15:0] == VLID
181*4882a593Smuzhiyun  *         all specified in rule)      |                    (specified in rule)
182*4882a593Smuzhiyun  *                                     v             && INGRESS PORT }
183*4882a593Smuzhiyun  *                               ------------
184*4882a593Smuzhiyun  *                    0 (PSFP)  /            \  1 (ARINC664)
185*4882a593Smuzhiyun  *                 +-----------/  VLLUPFORMAT \----------+
186*4882a593Smuzhiyun  *                 |           \    (fixed)   /          |
187*4882a593Smuzhiyun  *                 |            \            /           |
188*4882a593Smuzhiyun  *  0 (forwarding) v             ------------            |
189*4882a593Smuzhiyun  *           ------------                                |
190*4882a593Smuzhiyun  *          /            \  1 (QoS classification)       |
191*4882a593Smuzhiyun  *     +---/  ISCRITICAL  \-----------+                  |
192*4882a593Smuzhiyun  *     |   \  (per rule)  /           |                  |
193*4882a593Smuzhiyun  *     |    \            /   VLID taken from      VLID taken from
194*4882a593Smuzhiyun  *     v     ------------     index of rule       contents of rule
195*4882a593Smuzhiyun  *  select                     that matched         that matched
196*4882a593Smuzhiyun  * DESTPORTS                          |                  |
197*4882a593Smuzhiyun  *  |                                 +---------+--------+
198*4882a593Smuzhiyun  *  |                                           |
199*4882a593Smuzhiyun  *  |                                           v
200*4882a593Smuzhiyun  *  |                                     VL Forwarding
201*4882a593Smuzhiyun  *  |                                   (indexed by VLID)
202*4882a593Smuzhiyun  *  |                                      +---------+
203*4882a593Smuzhiyun  *  |                       +--------------|         |
204*4882a593Smuzhiyun  *  |                       |  select TYPE +---------+
205*4882a593Smuzhiyun  *  |                       v
206*4882a593Smuzhiyun  *  |   0 (rate      ------------    1 (time
207*4882a593Smuzhiyun  *  |  constrained) /            \   triggered)
208*4882a593Smuzhiyun  *  |       +------/     TYPE     \------------+
209*4882a593Smuzhiyun  *  |       |      \  (per VLID)  /            |
210*4882a593Smuzhiyun  *  |       v       \            /             v
211*4882a593Smuzhiyun  *  |  VL Policing   ------------         VL Policing
212*4882a593Smuzhiyun  *  | (indexed by VLID)                (indexed by VLID)
213*4882a593Smuzhiyun  *  |  +---------+                        +---------+
214*4882a593Smuzhiyun  *  |  | TYPE=0  |                        | TYPE=1  |
215*4882a593Smuzhiyun  *  |  +---------+                        +---------+
216*4882a593Smuzhiyun  *  |  select SHARINDX                 select SHARINDX to
217*4882a593Smuzhiyun  *  |  to rate-limit                 re-enter VL Forwarding
218*4882a593Smuzhiyun  *  |  groups of VL's               with new VLID for egress
219*4882a593Smuzhiyun  *  |  to same quota                           |
220*4882a593Smuzhiyun  *  |       |                                  |
221*4882a593Smuzhiyun  *  |  select MAXLEN -> exceed => drop    select MAXLEN -> exceed => drop
222*4882a593Smuzhiyun  *  |       |                                  |
223*4882a593Smuzhiyun  *  |       v                                  v
224*4882a593Smuzhiyun  *  |  VL Forwarding                      VL Forwarding
225*4882a593Smuzhiyun  *  | (indexed by SHARINDX)             (indexed by SHARINDX)
226*4882a593Smuzhiyun  *  |  +---------+                        +---------+
227*4882a593Smuzhiyun  *  |  | TYPE=0  |                        | TYPE=1  |
228*4882a593Smuzhiyun  *  |  +---------+                        +---------+
229*4882a593Smuzhiyun  *  |  select PRIORITY,                 select PRIORITY,
230*4882a593Smuzhiyun  *  | PARTITION, DESTPORTS            PARTITION, DESTPORTS
231*4882a593Smuzhiyun  *  |       |                                  |
232*4882a593Smuzhiyun  *  |       v                                  v
233*4882a593Smuzhiyun  *  |  VL Policing                        VL Policing
234*4882a593Smuzhiyun  *  | (indexed by SHARINDX)           (indexed by SHARINDX)
235*4882a593Smuzhiyun  *  |  +---------+                        +---------+
236*4882a593Smuzhiyun  *  |  | TYPE=0  |                        | TYPE=1  |
237*4882a593Smuzhiyun  *  |  +---------+                        +---------+
238*4882a593Smuzhiyun  *  |       |                                  |
239*4882a593Smuzhiyun  *  |       v                                  |
240*4882a593Smuzhiyun  *  |  select BAG, -> exceed => drop           |
241*4882a593Smuzhiyun  *  |    JITTER                                v
242*4882a593Smuzhiyun  *  |       |             ----------------------------------------------
243*4882a593Smuzhiyun  *  |       |            /    Reception Window is open for this VL      \
244*4882a593Smuzhiyun  *  |       |           /    (the Schedule Table executes an entry i     \
245*4882a593Smuzhiyun  *  |       |          /   M <= i < N, for which these conditions hold):  \ no
246*4882a593Smuzhiyun  *  |       |    +----/                                                    \-+
247*4882a593Smuzhiyun  *  |       |    |yes \       WINST[M] == 1 && WINSTINDEX[M] == VLID       / |
248*4882a593Smuzhiyun  *  |       |    |     \     WINEND[N] == 1 && WINSTINDEX[N] == VLID      /  |
249*4882a593Smuzhiyun  *  |       |    |      \                                                /   |
250*4882a593Smuzhiyun  *  |       |    |       \ (the VL window has opened and not yet closed)/    |
251*4882a593Smuzhiyun  *  |       |    |        ----------------------------------------------     |
252*4882a593Smuzhiyun  *  |       |    v                                                           v
253*4882a593Smuzhiyun  *  |       |  dispatch to DESTPORTS when the Schedule Table               drop
254*4882a593Smuzhiyun  *  |       |  executes an entry i with TXEN == 1 && VLINDEX == i
255*4882a593Smuzhiyun  *  v       v
256*4882a593Smuzhiyun  * dispatch immediately to DESTPORTS
257*4882a593Smuzhiyun  *
258*4882a593Smuzhiyun  * The per-port classification key is always composed of {DMAC, VID, PCP} and
259*4882a593Smuzhiyun  * is non-maskable. This 'looks like' the NULL stream identification function
260*4882a593Smuzhiyun  * from IEEE 802.1CB clause 6, except for the extra VLAN PCP. When the switch
261*4882a593Smuzhiyun  * ports operate as VLAN-unaware, we do allow the user to not specify the VLAN
262*4882a593Smuzhiyun  * ID and PCP, and then the port-based defaults will be used.
263*4882a593Smuzhiyun  *
264*4882a593Smuzhiyun  * In TTEthernet, routing is something that needs to be done manually for each
265*4882a593Smuzhiyun  * Virtual Link. So the flow action must always include one of:
266*4882a593Smuzhiyun  * a. 'redirect', 'trap' or 'drop': select the egress port list
267*4882a593Smuzhiyun  * Additionally, the following actions may be applied on a Virtual Link,
268*4882a593Smuzhiyun  * turning it into 'critical' traffic:
269*4882a593Smuzhiyun  * b. 'police': turn it into a rate-constrained VL, with bandwidth limitation
270*4882a593Smuzhiyun  *    given by the maximum frame length, bandwidth allocation gap (BAG) and
271*4882a593Smuzhiyun  *    maximum jitter.
272*4882a593Smuzhiyun  * c. 'gate': turn it into a time-triggered VL, which can be only be received
273*4882a593Smuzhiyun  *    and forwarded according to a given schedule.
274*4882a593Smuzhiyun  */
275*4882a593Smuzhiyun 
sja1105_vl_key_lower(struct sja1105_vl_lookup_entry * a,struct sja1105_vl_lookup_entry * b)276*4882a593Smuzhiyun static bool sja1105_vl_key_lower(struct sja1105_vl_lookup_entry *a,
277*4882a593Smuzhiyun 				 struct sja1105_vl_lookup_entry *b)
278*4882a593Smuzhiyun {
279*4882a593Smuzhiyun 	if (a->macaddr < b->macaddr)
280*4882a593Smuzhiyun 		return true;
281*4882a593Smuzhiyun 	if (a->macaddr > b->macaddr)
282*4882a593Smuzhiyun 		return false;
283*4882a593Smuzhiyun 	if (a->vlanid < b->vlanid)
284*4882a593Smuzhiyun 		return true;
285*4882a593Smuzhiyun 	if (a->vlanid > b->vlanid)
286*4882a593Smuzhiyun 		return false;
287*4882a593Smuzhiyun 	if (a->port < b->port)
288*4882a593Smuzhiyun 		return true;
289*4882a593Smuzhiyun 	if (a->port > b->port)
290*4882a593Smuzhiyun 		return false;
291*4882a593Smuzhiyun 	if (a->vlanprior < b->vlanprior)
292*4882a593Smuzhiyun 		return true;
293*4882a593Smuzhiyun 	if (a->vlanprior > b->vlanprior)
294*4882a593Smuzhiyun 		return false;
295*4882a593Smuzhiyun 	/* Keys are equal */
296*4882a593Smuzhiyun 	return false;
297*4882a593Smuzhiyun }
298*4882a593Smuzhiyun 
sja1105_init_virtual_links(struct sja1105_private * priv,struct netlink_ext_ack * extack)299*4882a593Smuzhiyun static int sja1105_init_virtual_links(struct sja1105_private *priv,
300*4882a593Smuzhiyun 				      struct netlink_ext_ack *extack)
301*4882a593Smuzhiyun {
302*4882a593Smuzhiyun 	struct sja1105_vl_policing_entry *vl_policing;
303*4882a593Smuzhiyun 	struct sja1105_vl_forwarding_entry *vl_fwd;
304*4882a593Smuzhiyun 	struct sja1105_vl_lookup_entry *vl_lookup;
305*4882a593Smuzhiyun 	bool have_critical_virtual_links = false;
306*4882a593Smuzhiyun 	struct sja1105_table *table;
307*4882a593Smuzhiyun 	struct sja1105_rule *rule;
308*4882a593Smuzhiyun 	int num_virtual_links = 0;
309*4882a593Smuzhiyun 	int max_sharindx = 0;
310*4882a593Smuzhiyun 	int i, j, k;
311*4882a593Smuzhiyun 
312*4882a593Smuzhiyun 	/* Figure out the dimensioning of the problem */
313*4882a593Smuzhiyun 	list_for_each_entry(rule, &priv->flow_block.rules, list) {
314*4882a593Smuzhiyun 		if (rule->type != SJA1105_RULE_VL)
315*4882a593Smuzhiyun 			continue;
316*4882a593Smuzhiyun 		/* Each VL lookup entry matches on a single ingress port */
317*4882a593Smuzhiyun 		num_virtual_links += hweight_long(rule->port_mask);
318*4882a593Smuzhiyun 
319*4882a593Smuzhiyun 		if (rule->vl.type != SJA1105_VL_NONCRITICAL)
320*4882a593Smuzhiyun 			have_critical_virtual_links = true;
321*4882a593Smuzhiyun 		if (max_sharindx < rule->vl.sharindx)
322*4882a593Smuzhiyun 			max_sharindx = rule->vl.sharindx;
323*4882a593Smuzhiyun 	}
324*4882a593Smuzhiyun 
325*4882a593Smuzhiyun 	if (num_virtual_links > SJA1105_MAX_VL_LOOKUP_COUNT) {
326*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "Not enough VL entries available");
327*4882a593Smuzhiyun 		return -ENOSPC;
328*4882a593Smuzhiyun 	}
329*4882a593Smuzhiyun 
330*4882a593Smuzhiyun 	if (max_sharindx + 1 > SJA1105_MAX_VL_LOOKUP_COUNT) {
331*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "Policer index out of range");
332*4882a593Smuzhiyun 		return -ENOSPC;
333*4882a593Smuzhiyun 	}
334*4882a593Smuzhiyun 
335*4882a593Smuzhiyun 	max_sharindx = max_t(int, num_virtual_links, max_sharindx) + 1;
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun 	/* Discard previous VL Lookup Table */
338*4882a593Smuzhiyun 	table = &priv->static_config.tables[BLK_IDX_VL_LOOKUP];
339*4882a593Smuzhiyun 	if (table->entry_count) {
340*4882a593Smuzhiyun 		kfree(table->entries);
341*4882a593Smuzhiyun 		table->entry_count = 0;
342*4882a593Smuzhiyun 	}
343*4882a593Smuzhiyun 
344*4882a593Smuzhiyun 	/* Discard previous VL Policing Table */
345*4882a593Smuzhiyun 	table = &priv->static_config.tables[BLK_IDX_VL_POLICING];
346*4882a593Smuzhiyun 	if (table->entry_count) {
347*4882a593Smuzhiyun 		kfree(table->entries);
348*4882a593Smuzhiyun 		table->entry_count = 0;
349*4882a593Smuzhiyun 	}
350*4882a593Smuzhiyun 
351*4882a593Smuzhiyun 	/* Discard previous VL Forwarding Table */
352*4882a593Smuzhiyun 	table = &priv->static_config.tables[BLK_IDX_VL_FORWARDING];
353*4882a593Smuzhiyun 	if (table->entry_count) {
354*4882a593Smuzhiyun 		kfree(table->entries);
355*4882a593Smuzhiyun 		table->entry_count = 0;
356*4882a593Smuzhiyun 	}
357*4882a593Smuzhiyun 
358*4882a593Smuzhiyun 	/* Discard previous VL Forwarding Parameters Table */
359*4882a593Smuzhiyun 	table = &priv->static_config.tables[BLK_IDX_VL_FORWARDING_PARAMS];
360*4882a593Smuzhiyun 	if (table->entry_count) {
361*4882a593Smuzhiyun 		kfree(table->entries);
362*4882a593Smuzhiyun 		table->entry_count = 0;
363*4882a593Smuzhiyun 	}
364*4882a593Smuzhiyun 
365*4882a593Smuzhiyun 	/* Nothing to do */
366*4882a593Smuzhiyun 	if (!num_virtual_links)
367*4882a593Smuzhiyun 		return 0;
368*4882a593Smuzhiyun 
369*4882a593Smuzhiyun 	/* Pre-allocate space in the static config tables */
370*4882a593Smuzhiyun 
371*4882a593Smuzhiyun 	/* VL Lookup Table */
372*4882a593Smuzhiyun 	table = &priv->static_config.tables[BLK_IDX_VL_LOOKUP];
373*4882a593Smuzhiyun 	table->entries = kcalloc(num_virtual_links,
374*4882a593Smuzhiyun 				 table->ops->unpacked_entry_size,
375*4882a593Smuzhiyun 				 GFP_KERNEL);
376*4882a593Smuzhiyun 	if (!table->entries)
377*4882a593Smuzhiyun 		return -ENOMEM;
378*4882a593Smuzhiyun 	table->entry_count = num_virtual_links;
379*4882a593Smuzhiyun 	vl_lookup = table->entries;
380*4882a593Smuzhiyun 
381*4882a593Smuzhiyun 	k = 0;
382*4882a593Smuzhiyun 
383*4882a593Smuzhiyun 	list_for_each_entry(rule, &priv->flow_block.rules, list) {
384*4882a593Smuzhiyun 		unsigned long port;
385*4882a593Smuzhiyun 
386*4882a593Smuzhiyun 		if (rule->type != SJA1105_RULE_VL)
387*4882a593Smuzhiyun 			continue;
388*4882a593Smuzhiyun 
389*4882a593Smuzhiyun 		for_each_set_bit(port, &rule->port_mask, SJA1105_NUM_PORTS) {
390*4882a593Smuzhiyun 			vl_lookup[k].format = SJA1105_VL_FORMAT_PSFP;
391*4882a593Smuzhiyun 			vl_lookup[k].port = port;
392*4882a593Smuzhiyun 			vl_lookup[k].macaddr = rule->key.vl.dmac;
393*4882a593Smuzhiyun 			if (rule->key.type == SJA1105_KEY_VLAN_AWARE_VL) {
394*4882a593Smuzhiyun 				vl_lookup[k].vlanid = rule->key.vl.vid;
395*4882a593Smuzhiyun 				vl_lookup[k].vlanprior = rule->key.vl.pcp;
396*4882a593Smuzhiyun 			} else {
397*4882a593Smuzhiyun 				u16 vid = dsa_8021q_rx_vid(priv->ds, port);
398*4882a593Smuzhiyun 
399*4882a593Smuzhiyun 				vl_lookup[k].vlanid = vid;
400*4882a593Smuzhiyun 				vl_lookup[k].vlanprior = 0;
401*4882a593Smuzhiyun 			}
402*4882a593Smuzhiyun 			/* For critical VLs, the DESTPORTS mask is taken from
403*4882a593Smuzhiyun 			 * the VL Forwarding Table, so no point in putting it
404*4882a593Smuzhiyun 			 * in the VL Lookup Table
405*4882a593Smuzhiyun 			 */
406*4882a593Smuzhiyun 			if (rule->vl.type == SJA1105_VL_NONCRITICAL)
407*4882a593Smuzhiyun 				vl_lookup[k].destports = rule->vl.destports;
408*4882a593Smuzhiyun 			else
409*4882a593Smuzhiyun 				vl_lookup[k].iscritical = true;
410*4882a593Smuzhiyun 			vl_lookup[k].flow_cookie = rule->cookie;
411*4882a593Smuzhiyun 			k++;
412*4882a593Smuzhiyun 		}
413*4882a593Smuzhiyun 	}
414*4882a593Smuzhiyun 
415*4882a593Smuzhiyun 	/* UM10944.pdf chapter 4.2.3 VL Lookup table:
416*4882a593Smuzhiyun 	 * "the entries in the VL Lookup table must be sorted in ascending
417*4882a593Smuzhiyun 	 * order (i.e. the smallest value must be loaded first) according to
418*4882a593Smuzhiyun 	 * the following sort order: MACADDR, VLANID, PORT, VLANPRIOR."
419*4882a593Smuzhiyun 	 */
420*4882a593Smuzhiyun 	for (i = 0; i < num_virtual_links; i++) {
421*4882a593Smuzhiyun 		struct sja1105_vl_lookup_entry *a = &vl_lookup[i];
422*4882a593Smuzhiyun 
423*4882a593Smuzhiyun 		for (j = i + 1; j < num_virtual_links; j++) {
424*4882a593Smuzhiyun 			struct sja1105_vl_lookup_entry *b = &vl_lookup[j];
425*4882a593Smuzhiyun 
426*4882a593Smuzhiyun 			if (sja1105_vl_key_lower(b, a)) {
427*4882a593Smuzhiyun 				struct sja1105_vl_lookup_entry tmp = *a;
428*4882a593Smuzhiyun 
429*4882a593Smuzhiyun 				*a = *b;
430*4882a593Smuzhiyun 				*b = tmp;
431*4882a593Smuzhiyun 			}
432*4882a593Smuzhiyun 		}
433*4882a593Smuzhiyun 	}
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun 	if (!have_critical_virtual_links)
436*4882a593Smuzhiyun 		return 0;
437*4882a593Smuzhiyun 
438*4882a593Smuzhiyun 	/* VL Policing Table */
439*4882a593Smuzhiyun 	table = &priv->static_config.tables[BLK_IDX_VL_POLICING];
440*4882a593Smuzhiyun 	table->entries = kcalloc(max_sharindx, table->ops->unpacked_entry_size,
441*4882a593Smuzhiyun 				 GFP_KERNEL);
442*4882a593Smuzhiyun 	if (!table->entries)
443*4882a593Smuzhiyun 		return -ENOMEM;
444*4882a593Smuzhiyun 	table->entry_count = max_sharindx;
445*4882a593Smuzhiyun 	vl_policing = table->entries;
446*4882a593Smuzhiyun 
447*4882a593Smuzhiyun 	/* VL Forwarding Table */
448*4882a593Smuzhiyun 	table = &priv->static_config.tables[BLK_IDX_VL_FORWARDING];
449*4882a593Smuzhiyun 	table->entries = kcalloc(max_sharindx, table->ops->unpacked_entry_size,
450*4882a593Smuzhiyun 				 GFP_KERNEL);
451*4882a593Smuzhiyun 	if (!table->entries)
452*4882a593Smuzhiyun 		return -ENOMEM;
453*4882a593Smuzhiyun 	table->entry_count = max_sharindx;
454*4882a593Smuzhiyun 	vl_fwd = table->entries;
455*4882a593Smuzhiyun 
456*4882a593Smuzhiyun 	/* VL Forwarding Parameters Table */
457*4882a593Smuzhiyun 	table = &priv->static_config.tables[BLK_IDX_VL_FORWARDING_PARAMS];
458*4882a593Smuzhiyun 	table->entries = kcalloc(1, table->ops->unpacked_entry_size,
459*4882a593Smuzhiyun 				 GFP_KERNEL);
460*4882a593Smuzhiyun 	if (!table->entries)
461*4882a593Smuzhiyun 		return -ENOMEM;
462*4882a593Smuzhiyun 	table->entry_count = 1;
463*4882a593Smuzhiyun 
464*4882a593Smuzhiyun 	for (i = 0; i < num_virtual_links; i++) {
465*4882a593Smuzhiyun 		unsigned long cookie = vl_lookup[i].flow_cookie;
466*4882a593Smuzhiyun 		struct sja1105_rule *rule = sja1105_rule_find(priv, cookie);
467*4882a593Smuzhiyun 
468*4882a593Smuzhiyun 		if (rule->vl.type == SJA1105_VL_NONCRITICAL)
469*4882a593Smuzhiyun 			continue;
470*4882a593Smuzhiyun 		if (rule->vl.type == SJA1105_VL_TIME_TRIGGERED) {
471*4882a593Smuzhiyun 			int sharindx = rule->vl.sharindx;
472*4882a593Smuzhiyun 
473*4882a593Smuzhiyun 			vl_policing[i].type = 1;
474*4882a593Smuzhiyun 			vl_policing[i].sharindx = sharindx;
475*4882a593Smuzhiyun 			vl_policing[i].maxlen = rule->vl.maxlen;
476*4882a593Smuzhiyun 			vl_policing[sharindx].type = 1;
477*4882a593Smuzhiyun 
478*4882a593Smuzhiyun 			vl_fwd[i].type = 1;
479*4882a593Smuzhiyun 			vl_fwd[sharindx].type = 1;
480*4882a593Smuzhiyun 			vl_fwd[sharindx].priority = rule->vl.ipv;
481*4882a593Smuzhiyun 			vl_fwd[sharindx].partition = 0;
482*4882a593Smuzhiyun 			vl_fwd[sharindx].destports = rule->vl.destports;
483*4882a593Smuzhiyun 		}
484*4882a593Smuzhiyun 	}
485*4882a593Smuzhiyun 
486*4882a593Smuzhiyun 	sja1105_frame_memory_partitioning(priv);
487*4882a593Smuzhiyun 
488*4882a593Smuzhiyun 	return 0;
489*4882a593Smuzhiyun }
490*4882a593Smuzhiyun 
sja1105_vl_redirect(struct sja1105_private * priv,int port,struct netlink_ext_ack * extack,unsigned long cookie,struct sja1105_key * key,unsigned long destports,bool append)491*4882a593Smuzhiyun int sja1105_vl_redirect(struct sja1105_private *priv, int port,
492*4882a593Smuzhiyun 			struct netlink_ext_ack *extack, unsigned long cookie,
493*4882a593Smuzhiyun 			struct sja1105_key *key, unsigned long destports,
494*4882a593Smuzhiyun 			bool append)
495*4882a593Smuzhiyun {
496*4882a593Smuzhiyun 	struct sja1105_rule *rule = sja1105_rule_find(priv, cookie);
497*4882a593Smuzhiyun 	int rc;
498*4882a593Smuzhiyun 
499*4882a593Smuzhiyun 	if (priv->vlan_state == SJA1105_VLAN_UNAWARE &&
500*4882a593Smuzhiyun 	    key->type != SJA1105_KEY_VLAN_UNAWARE_VL) {
501*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack,
502*4882a593Smuzhiyun 				   "Can only redirect based on DMAC");
503*4882a593Smuzhiyun 		return -EOPNOTSUPP;
504*4882a593Smuzhiyun 	} else if ((priv->vlan_state == SJA1105_VLAN_BEST_EFFORT ||
505*4882a593Smuzhiyun 		    priv->vlan_state == SJA1105_VLAN_FILTERING_FULL) &&
506*4882a593Smuzhiyun 		   key->type != SJA1105_KEY_VLAN_AWARE_VL) {
507*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack,
508*4882a593Smuzhiyun 				   "Can only redirect based on {DMAC, VID, PCP}");
509*4882a593Smuzhiyun 		return -EOPNOTSUPP;
510*4882a593Smuzhiyun 	}
511*4882a593Smuzhiyun 
512*4882a593Smuzhiyun 	if (!rule) {
513*4882a593Smuzhiyun 		rule = kzalloc(sizeof(*rule), GFP_KERNEL);
514*4882a593Smuzhiyun 		if (!rule)
515*4882a593Smuzhiyun 			return -ENOMEM;
516*4882a593Smuzhiyun 
517*4882a593Smuzhiyun 		rule->cookie = cookie;
518*4882a593Smuzhiyun 		rule->type = SJA1105_RULE_VL;
519*4882a593Smuzhiyun 		rule->key = *key;
520*4882a593Smuzhiyun 		list_add(&rule->list, &priv->flow_block.rules);
521*4882a593Smuzhiyun 	}
522*4882a593Smuzhiyun 
523*4882a593Smuzhiyun 	rule->port_mask |= BIT(port);
524*4882a593Smuzhiyun 	if (append)
525*4882a593Smuzhiyun 		rule->vl.destports |= destports;
526*4882a593Smuzhiyun 	else
527*4882a593Smuzhiyun 		rule->vl.destports = destports;
528*4882a593Smuzhiyun 
529*4882a593Smuzhiyun 	rc = sja1105_init_virtual_links(priv, extack);
530*4882a593Smuzhiyun 	if (rc) {
531*4882a593Smuzhiyun 		rule->port_mask &= ~BIT(port);
532*4882a593Smuzhiyun 		if (!rule->port_mask) {
533*4882a593Smuzhiyun 			list_del(&rule->list);
534*4882a593Smuzhiyun 			kfree(rule);
535*4882a593Smuzhiyun 		}
536*4882a593Smuzhiyun 	}
537*4882a593Smuzhiyun 
538*4882a593Smuzhiyun 	return rc;
539*4882a593Smuzhiyun }
540*4882a593Smuzhiyun 
sja1105_vl_delete(struct sja1105_private * priv,int port,struct sja1105_rule * rule,struct netlink_ext_ack * extack)541*4882a593Smuzhiyun int sja1105_vl_delete(struct sja1105_private *priv, int port,
542*4882a593Smuzhiyun 		      struct sja1105_rule *rule, struct netlink_ext_ack *extack)
543*4882a593Smuzhiyun {
544*4882a593Smuzhiyun 	int rc;
545*4882a593Smuzhiyun 
546*4882a593Smuzhiyun 	rule->port_mask &= ~BIT(port);
547*4882a593Smuzhiyun 	if (!rule->port_mask) {
548*4882a593Smuzhiyun 		list_del(&rule->list);
549*4882a593Smuzhiyun 		kfree(rule);
550*4882a593Smuzhiyun 	}
551*4882a593Smuzhiyun 
552*4882a593Smuzhiyun 	rc = sja1105_compose_gating_subschedule(priv, extack);
553*4882a593Smuzhiyun 	if (rc)
554*4882a593Smuzhiyun 		return rc;
555*4882a593Smuzhiyun 
556*4882a593Smuzhiyun 	rc = sja1105_init_virtual_links(priv, extack);
557*4882a593Smuzhiyun 	if (rc)
558*4882a593Smuzhiyun 		return rc;
559*4882a593Smuzhiyun 
560*4882a593Smuzhiyun 	rc = sja1105_init_scheduling(priv);
561*4882a593Smuzhiyun 	if (rc < 0)
562*4882a593Smuzhiyun 		return rc;
563*4882a593Smuzhiyun 
564*4882a593Smuzhiyun 	return sja1105_static_config_reload(priv, SJA1105_VIRTUAL_LINKS);
565*4882a593Smuzhiyun }
566*4882a593Smuzhiyun 
sja1105_vl_gate(struct sja1105_private * priv,int port,struct netlink_ext_ack * extack,unsigned long cookie,struct sja1105_key * key,u32 index,s32 prio,u64 base_time,u64 cycle_time,u64 cycle_time_ext,u32 num_entries,struct action_gate_entry * entries)567*4882a593Smuzhiyun int sja1105_vl_gate(struct sja1105_private *priv, int port,
568*4882a593Smuzhiyun 		    struct netlink_ext_ack *extack, unsigned long cookie,
569*4882a593Smuzhiyun 		    struct sja1105_key *key, u32 index, s32 prio,
570*4882a593Smuzhiyun 		    u64 base_time, u64 cycle_time, u64 cycle_time_ext,
571*4882a593Smuzhiyun 		    u32 num_entries, struct action_gate_entry *entries)
572*4882a593Smuzhiyun {
573*4882a593Smuzhiyun 	struct sja1105_rule *rule = sja1105_rule_find(priv, cookie);
574*4882a593Smuzhiyun 	int ipv = -1;
575*4882a593Smuzhiyun 	int i, rc;
576*4882a593Smuzhiyun 	s32 rem;
577*4882a593Smuzhiyun 
578*4882a593Smuzhiyun 	if (cycle_time_ext) {
579*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack,
580*4882a593Smuzhiyun 				   "Cycle time extension not supported");
581*4882a593Smuzhiyun 		return -EOPNOTSUPP;
582*4882a593Smuzhiyun 	}
583*4882a593Smuzhiyun 
584*4882a593Smuzhiyun 	div_s64_rem(base_time, sja1105_delta_to_ns(1), &rem);
585*4882a593Smuzhiyun 	if (rem) {
586*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack,
587*4882a593Smuzhiyun 				   "Base time must be multiple of 200 ns");
588*4882a593Smuzhiyun 		return -ERANGE;
589*4882a593Smuzhiyun 	}
590*4882a593Smuzhiyun 
591*4882a593Smuzhiyun 	div_s64_rem(cycle_time, sja1105_delta_to_ns(1), &rem);
592*4882a593Smuzhiyun 	if (rem) {
593*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack,
594*4882a593Smuzhiyun 				   "Cycle time must be multiple of 200 ns");
595*4882a593Smuzhiyun 		return -ERANGE;
596*4882a593Smuzhiyun 	}
597*4882a593Smuzhiyun 
598*4882a593Smuzhiyun 	if (priv->vlan_state == SJA1105_VLAN_UNAWARE &&
599*4882a593Smuzhiyun 	    key->type != SJA1105_KEY_VLAN_UNAWARE_VL) {
600*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack,
601*4882a593Smuzhiyun 				   "Can only gate based on DMAC");
602*4882a593Smuzhiyun 		return -EOPNOTSUPP;
603*4882a593Smuzhiyun 	} else if ((priv->vlan_state == SJA1105_VLAN_BEST_EFFORT ||
604*4882a593Smuzhiyun 		    priv->vlan_state == SJA1105_VLAN_FILTERING_FULL) &&
605*4882a593Smuzhiyun 		   key->type != SJA1105_KEY_VLAN_AWARE_VL) {
606*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack,
607*4882a593Smuzhiyun 				   "Can only gate based on {DMAC, VID, PCP}");
608*4882a593Smuzhiyun 		return -EOPNOTSUPP;
609*4882a593Smuzhiyun 	}
610*4882a593Smuzhiyun 
611*4882a593Smuzhiyun 	if (!rule) {
612*4882a593Smuzhiyun 		rule = kzalloc(sizeof(*rule), GFP_KERNEL);
613*4882a593Smuzhiyun 		if (!rule)
614*4882a593Smuzhiyun 			return -ENOMEM;
615*4882a593Smuzhiyun 
616*4882a593Smuzhiyun 		list_add(&rule->list, &priv->flow_block.rules);
617*4882a593Smuzhiyun 		rule->cookie = cookie;
618*4882a593Smuzhiyun 		rule->type = SJA1105_RULE_VL;
619*4882a593Smuzhiyun 		rule->key = *key;
620*4882a593Smuzhiyun 		rule->vl.type = SJA1105_VL_TIME_TRIGGERED;
621*4882a593Smuzhiyun 		rule->vl.sharindx = index;
622*4882a593Smuzhiyun 		rule->vl.base_time = base_time;
623*4882a593Smuzhiyun 		rule->vl.cycle_time = cycle_time;
624*4882a593Smuzhiyun 		rule->vl.num_entries = num_entries;
625*4882a593Smuzhiyun 		rule->vl.entries = kcalloc(num_entries,
626*4882a593Smuzhiyun 					   sizeof(struct action_gate_entry),
627*4882a593Smuzhiyun 					   GFP_KERNEL);
628*4882a593Smuzhiyun 		if (!rule->vl.entries) {
629*4882a593Smuzhiyun 			rc = -ENOMEM;
630*4882a593Smuzhiyun 			goto out;
631*4882a593Smuzhiyun 		}
632*4882a593Smuzhiyun 
633*4882a593Smuzhiyun 		for (i = 0; i < num_entries; i++) {
634*4882a593Smuzhiyun 			div_s64_rem(entries[i].interval,
635*4882a593Smuzhiyun 				    sja1105_delta_to_ns(1), &rem);
636*4882a593Smuzhiyun 			if (rem) {
637*4882a593Smuzhiyun 				NL_SET_ERR_MSG_MOD(extack,
638*4882a593Smuzhiyun 						   "Interval must be multiple of 200 ns");
639*4882a593Smuzhiyun 				rc = -ERANGE;
640*4882a593Smuzhiyun 				goto out;
641*4882a593Smuzhiyun 			}
642*4882a593Smuzhiyun 
643*4882a593Smuzhiyun 			if (!entries[i].interval) {
644*4882a593Smuzhiyun 				NL_SET_ERR_MSG_MOD(extack,
645*4882a593Smuzhiyun 						   "Interval cannot be zero");
646*4882a593Smuzhiyun 				rc = -ERANGE;
647*4882a593Smuzhiyun 				goto out;
648*4882a593Smuzhiyun 			}
649*4882a593Smuzhiyun 
650*4882a593Smuzhiyun 			if (ns_to_sja1105_delta(entries[i].interval) >
651*4882a593Smuzhiyun 			    SJA1105_TAS_MAX_DELTA) {
652*4882a593Smuzhiyun 				NL_SET_ERR_MSG_MOD(extack,
653*4882a593Smuzhiyun 						   "Maximum interval is 52 ms");
654*4882a593Smuzhiyun 				rc = -ERANGE;
655*4882a593Smuzhiyun 				goto out;
656*4882a593Smuzhiyun 			}
657*4882a593Smuzhiyun 
658*4882a593Smuzhiyun 			if (entries[i].maxoctets != -1) {
659*4882a593Smuzhiyun 				NL_SET_ERR_MSG_MOD(extack,
660*4882a593Smuzhiyun 						   "Cannot offload IntervalOctetMax");
661*4882a593Smuzhiyun 				rc = -EOPNOTSUPP;
662*4882a593Smuzhiyun 				goto out;
663*4882a593Smuzhiyun 			}
664*4882a593Smuzhiyun 
665*4882a593Smuzhiyun 			if (ipv == -1) {
666*4882a593Smuzhiyun 				ipv = entries[i].ipv;
667*4882a593Smuzhiyun 			} else if (ipv != entries[i].ipv) {
668*4882a593Smuzhiyun 				NL_SET_ERR_MSG_MOD(extack,
669*4882a593Smuzhiyun 						   "Only support a single IPV per VL");
670*4882a593Smuzhiyun 				rc = -EOPNOTSUPP;
671*4882a593Smuzhiyun 				goto out;
672*4882a593Smuzhiyun 			}
673*4882a593Smuzhiyun 
674*4882a593Smuzhiyun 			rule->vl.entries[i] = entries[i];
675*4882a593Smuzhiyun 		}
676*4882a593Smuzhiyun 
677*4882a593Smuzhiyun 		if (ipv == -1) {
678*4882a593Smuzhiyun 			if (key->type == SJA1105_KEY_VLAN_AWARE_VL)
679*4882a593Smuzhiyun 				ipv = key->vl.pcp;
680*4882a593Smuzhiyun 			else
681*4882a593Smuzhiyun 				ipv = 0;
682*4882a593Smuzhiyun 		}
683*4882a593Smuzhiyun 
684*4882a593Smuzhiyun 		/* TODO: support per-flow MTU */
685*4882a593Smuzhiyun 		rule->vl.maxlen = VLAN_ETH_FRAME_LEN + ETH_FCS_LEN;
686*4882a593Smuzhiyun 		rule->vl.ipv = ipv;
687*4882a593Smuzhiyun 	}
688*4882a593Smuzhiyun 
689*4882a593Smuzhiyun 	rule->port_mask |= BIT(port);
690*4882a593Smuzhiyun 
691*4882a593Smuzhiyun 	rc = sja1105_compose_gating_subschedule(priv, extack);
692*4882a593Smuzhiyun 	if (rc)
693*4882a593Smuzhiyun 		goto out;
694*4882a593Smuzhiyun 
695*4882a593Smuzhiyun 	rc = sja1105_init_virtual_links(priv, extack);
696*4882a593Smuzhiyun 	if (rc)
697*4882a593Smuzhiyun 		goto out;
698*4882a593Smuzhiyun 
699*4882a593Smuzhiyun 	if (sja1105_gating_check_conflicts(priv, -1, extack)) {
700*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "Conflict with tc-taprio schedule");
701*4882a593Smuzhiyun 		rc = -ERANGE;
702*4882a593Smuzhiyun 		goto out;
703*4882a593Smuzhiyun 	}
704*4882a593Smuzhiyun 
705*4882a593Smuzhiyun out:
706*4882a593Smuzhiyun 	if (rc) {
707*4882a593Smuzhiyun 		rule->port_mask &= ~BIT(port);
708*4882a593Smuzhiyun 		if (!rule->port_mask) {
709*4882a593Smuzhiyun 			list_del(&rule->list);
710*4882a593Smuzhiyun 			kfree(rule->vl.entries);
711*4882a593Smuzhiyun 			kfree(rule);
712*4882a593Smuzhiyun 		}
713*4882a593Smuzhiyun 	}
714*4882a593Smuzhiyun 
715*4882a593Smuzhiyun 	return rc;
716*4882a593Smuzhiyun }
717*4882a593Smuzhiyun 
sja1105_find_vlid(struct sja1105_private * priv,int port,struct sja1105_key * key)718*4882a593Smuzhiyun static int sja1105_find_vlid(struct sja1105_private *priv, int port,
719*4882a593Smuzhiyun 			     struct sja1105_key *key)
720*4882a593Smuzhiyun {
721*4882a593Smuzhiyun 	struct sja1105_vl_lookup_entry *vl_lookup;
722*4882a593Smuzhiyun 	struct sja1105_table *table;
723*4882a593Smuzhiyun 	int i;
724*4882a593Smuzhiyun 
725*4882a593Smuzhiyun 	if (WARN_ON(key->type != SJA1105_KEY_VLAN_AWARE_VL &&
726*4882a593Smuzhiyun 		    key->type != SJA1105_KEY_VLAN_UNAWARE_VL))
727*4882a593Smuzhiyun 		return -1;
728*4882a593Smuzhiyun 
729*4882a593Smuzhiyun 	table = &priv->static_config.tables[BLK_IDX_VL_LOOKUP];
730*4882a593Smuzhiyun 	vl_lookup = table->entries;
731*4882a593Smuzhiyun 
732*4882a593Smuzhiyun 	for (i = 0; i < table->entry_count; i++) {
733*4882a593Smuzhiyun 		if (key->type == SJA1105_KEY_VLAN_AWARE_VL) {
734*4882a593Smuzhiyun 			if (vl_lookup[i].port == port &&
735*4882a593Smuzhiyun 			    vl_lookup[i].macaddr == key->vl.dmac &&
736*4882a593Smuzhiyun 			    vl_lookup[i].vlanid == key->vl.vid &&
737*4882a593Smuzhiyun 			    vl_lookup[i].vlanprior == key->vl.pcp)
738*4882a593Smuzhiyun 				return i;
739*4882a593Smuzhiyun 		} else {
740*4882a593Smuzhiyun 			if (vl_lookup[i].port == port &&
741*4882a593Smuzhiyun 			    vl_lookup[i].macaddr == key->vl.dmac)
742*4882a593Smuzhiyun 				return i;
743*4882a593Smuzhiyun 		}
744*4882a593Smuzhiyun 	}
745*4882a593Smuzhiyun 
746*4882a593Smuzhiyun 	return -1;
747*4882a593Smuzhiyun }
748*4882a593Smuzhiyun 
sja1105_vl_stats(struct sja1105_private * priv,int port,struct sja1105_rule * rule,struct flow_stats * stats,struct netlink_ext_ack * extack)749*4882a593Smuzhiyun int sja1105_vl_stats(struct sja1105_private *priv, int port,
750*4882a593Smuzhiyun 		     struct sja1105_rule *rule, struct flow_stats *stats,
751*4882a593Smuzhiyun 		     struct netlink_ext_ack *extack)
752*4882a593Smuzhiyun {
753*4882a593Smuzhiyun 	const struct sja1105_regs *regs = priv->info->regs;
754*4882a593Smuzhiyun 	u8 buf[SJA1105_SIZE_VL_STATUS] = {0};
755*4882a593Smuzhiyun 	u64 unreleased;
756*4882a593Smuzhiyun 	u64 timingerr;
757*4882a593Smuzhiyun 	u64 lengtherr;
758*4882a593Smuzhiyun 	int vlid, rc;
759*4882a593Smuzhiyun 	u64 pkts;
760*4882a593Smuzhiyun 
761*4882a593Smuzhiyun 	if (rule->vl.type != SJA1105_VL_TIME_TRIGGERED)
762*4882a593Smuzhiyun 		return 0;
763*4882a593Smuzhiyun 
764*4882a593Smuzhiyun 	vlid = sja1105_find_vlid(priv, port, &rule->key);
765*4882a593Smuzhiyun 	if (vlid < 0)
766*4882a593Smuzhiyun 		return 0;
767*4882a593Smuzhiyun 
768*4882a593Smuzhiyun 	rc = sja1105_xfer_buf(priv, SPI_READ, regs->vl_status + 2 * vlid, buf,
769*4882a593Smuzhiyun 			      SJA1105_SIZE_VL_STATUS);
770*4882a593Smuzhiyun 	if (rc) {
771*4882a593Smuzhiyun 		NL_SET_ERR_MSG_MOD(extack, "SPI access failed");
772*4882a593Smuzhiyun 		return rc;
773*4882a593Smuzhiyun 	}
774*4882a593Smuzhiyun 
775*4882a593Smuzhiyun 	sja1105_unpack(buf, &timingerr,  31, 16, SJA1105_SIZE_VL_STATUS);
776*4882a593Smuzhiyun 	sja1105_unpack(buf, &unreleased, 15,  0, SJA1105_SIZE_VL_STATUS);
777*4882a593Smuzhiyun 	sja1105_unpack(buf, &lengtherr,  47, 32, SJA1105_SIZE_VL_STATUS);
778*4882a593Smuzhiyun 
779*4882a593Smuzhiyun 	pkts = timingerr + unreleased + lengtherr;
780*4882a593Smuzhiyun 
781*4882a593Smuzhiyun 	flow_stats_update(stats, 0, pkts - rule->vl.stats.pkts, 0,
782*4882a593Smuzhiyun 			  jiffies - rule->vl.stats.lastused,
783*4882a593Smuzhiyun 			  FLOW_ACTION_HW_STATS_IMMEDIATE);
784*4882a593Smuzhiyun 
785*4882a593Smuzhiyun 	rule->vl.stats.pkts = pkts;
786*4882a593Smuzhiyun 	rule->vl.stats.lastused = jiffies;
787*4882a593Smuzhiyun 
788*4882a593Smuzhiyun 	return 0;
789*4882a593Smuzhiyun }
790