18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/* Copyright 2020, NXP Semiconductors
38c2ecf20Sopenharmony_ci */
48c2ecf20Sopenharmony_ci#include <net/tc_act/tc_gate.h>
58c2ecf20Sopenharmony_ci#include <linux/dsa/8021q.h>
68c2ecf20Sopenharmony_ci#include "sja1105_vl.h"
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#define SJA1105_SIZE_VL_STATUS			8
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci/* Insert into the global gate list, sorted by gate action time. */
118c2ecf20Sopenharmony_cistatic int sja1105_insert_gate_entry(struct sja1105_gating_config *gating_cfg,
128c2ecf20Sopenharmony_ci				     struct sja1105_rule *rule,
138c2ecf20Sopenharmony_ci				     u8 gate_state, s64 entry_time,
148c2ecf20Sopenharmony_ci				     struct netlink_ext_ack *extack)
158c2ecf20Sopenharmony_ci{
168c2ecf20Sopenharmony_ci	struct sja1105_gate_entry *e;
178c2ecf20Sopenharmony_ci	int rc;
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci	e = kzalloc(sizeof(*e), GFP_KERNEL);
208c2ecf20Sopenharmony_ci	if (!e)
218c2ecf20Sopenharmony_ci		return -ENOMEM;
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci	e->rule = rule;
248c2ecf20Sopenharmony_ci	e->gate_state = gate_state;
258c2ecf20Sopenharmony_ci	e->interval = entry_time;
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci	if (list_empty(&gating_cfg->entries)) {
288c2ecf20Sopenharmony_ci		list_add(&e->list, &gating_cfg->entries);
298c2ecf20Sopenharmony_ci	} else {
308c2ecf20Sopenharmony_ci		struct sja1105_gate_entry *p;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci		list_for_each_entry(p, &gating_cfg->entries, list) {
338c2ecf20Sopenharmony_ci			if (p->interval == e->interval) {
348c2ecf20Sopenharmony_ci				NL_SET_ERR_MSG_MOD(extack,
358c2ecf20Sopenharmony_ci						   "Gate conflict");
368c2ecf20Sopenharmony_ci				rc = -EBUSY;
378c2ecf20Sopenharmony_ci				goto err;
388c2ecf20Sopenharmony_ci			}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci			if (e->interval < p->interval)
418c2ecf20Sopenharmony_ci				break;
428c2ecf20Sopenharmony_ci		}
438c2ecf20Sopenharmony_ci		list_add(&e->list, p->list.prev);
448c2ecf20Sopenharmony_ci	}
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	gating_cfg->num_entries++;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	return 0;
498c2ecf20Sopenharmony_cierr:
508c2ecf20Sopenharmony_ci	kfree(e);
518c2ecf20Sopenharmony_ci	return rc;
528c2ecf20Sopenharmony_ci}
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci/* The gate entries contain absolute times in their e->interval field. Convert
558c2ecf20Sopenharmony_ci * that to proper intervals (i.e. "0, 5, 10, 15" to "5, 5, 5, 5").
568c2ecf20Sopenharmony_ci */
578c2ecf20Sopenharmony_cistatic void
588c2ecf20Sopenharmony_cisja1105_gating_cfg_time_to_interval(struct sja1105_gating_config *gating_cfg,
598c2ecf20Sopenharmony_ci				    u64 cycle_time)
608c2ecf20Sopenharmony_ci{
618c2ecf20Sopenharmony_ci	struct sja1105_gate_entry *last_e;
628c2ecf20Sopenharmony_ci	struct sja1105_gate_entry *e;
638c2ecf20Sopenharmony_ci	struct list_head *prev;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	list_for_each_entry(e, &gating_cfg->entries, list) {
668c2ecf20Sopenharmony_ci		struct sja1105_gate_entry *p;
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci		prev = e->list.prev;
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_ci		if (prev == &gating_cfg->entries)
718c2ecf20Sopenharmony_ci			continue;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci		p = list_entry(prev, struct sja1105_gate_entry, list);
748c2ecf20Sopenharmony_ci		p->interval = e->interval - p->interval;
758c2ecf20Sopenharmony_ci	}
768c2ecf20Sopenharmony_ci	last_e = list_last_entry(&gating_cfg->entries,
778c2ecf20Sopenharmony_ci				 struct sja1105_gate_entry, list);
788c2ecf20Sopenharmony_ci	last_e->interval = cycle_time - last_e->interval;
798c2ecf20Sopenharmony_ci}
808c2ecf20Sopenharmony_ci
818c2ecf20Sopenharmony_cistatic void sja1105_free_gating_config(struct sja1105_gating_config *gating_cfg)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	struct sja1105_gate_entry *e, *n;
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	list_for_each_entry_safe(e, n, &gating_cfg->entries, list) {
868c2ecf20Sopenharmony_ci		list_del(&e->list);
878c2ecf20Sopenharmony_ci		kfree(e);
888c2ecf20Sopenharmony_ci	}
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic int sja1105_compose_gating_subschedule(struct sja1105_private *priv,
928c2ecf20Sopenharmony_ci					      struct netlink_ext_ack *extack)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	struct sja1105_gating_config *gating_cfg = &priv->tas_data.gating_cfg;
958c2ecf20Sopenharmony_ci	struct sja1105_rule *rule;
968c2ecf20Sopenharmony_ci	s64 max_cycle_time = 0;
978c2ecf20Sopenharmony_ci	s64 its_base_time = 0;
988c2ecf20Sopenharmony_ci	int i, rc = 0;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci	sja1105_free_gating_config(gating_cfg);
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci	list_for_each_entry(rule, &priv->flow_block.rules, list) {
1038c2ecf20Sopenharmony_ci		if (rule->type != SJA1105_RULE_VL)
1048c2ecf20Sopenharmony_ci			continue;
1058c2ecf20Sopenharmony_ci		if (rule->vl.type != SJA1105_VL_TIME_TRIGGERED)
1068c2ecf20Sopenharmony_ci			continue;
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci		if (max_cycle_time < rule->vl.cycle_time) {
1098c2ecf20Sopenharmony_ci			max_cycle_time = rule->vl.cycle_time;
1108c2ecf20Sopenharmony_ci			its_base_time = rule->vl.base_time;
1118c2ecf20Sopenharmony_ci		}
1128c2ecf20Sopenharmony_ci	}
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	if (!max_cycle_time)
1158c2ecf20Sopenharmony_ci		return 0;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci	dev_dbg(priv->ds->dev, "max_cycle_time %lld its_base_time %lld\n",
1188c2ecf20Sopenharmony_ci		max_cycle_time, its_base_time);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	gating_cfg->base_time = its_base_time;
1218c2ecf20Sopenharmony_ci	gating_cfg->cycle_time = max_cycle_time;
1228c2ecf20Sopenharmony_ci	gating_cfg->num_entries = 0;
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_ci	list_for_each_entry(rule, &priv->flow_block.rules, list) {
1258c2ecf20Sopenharmony_ci		s64 time;
1268c2ecf20Sopenharmony_ci		s64 rbt;
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_ci		if (rule->type != SJA1105_RULE_VL)
1298c2ecf20Sopenharmony_ci			continue;
1308c2ecf20Sopenharmony_ci		if (rule->vl.type != SJA1105_VL_TIME_TRIGGERED)
1318c2ecf20Sopenharmony_ci			continue;
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci		/* Calculate the difference between this gating schedule's
1348c2ecf20Sopenharmony_ci		 * base time, and the base time of the gating schedule with the
1358c2ecf20Sopenharmony_ci		 * longest cycle time. We call it the relative base time (rbt).
1368c2ecf20Sopenharmony_ci		 */
1378c2ecf20Sopenharmony_ci		rbt = future_base_time(rule->vl.base_time, rule->vl.cycle_time,
1388c2ecf20Sopenharmony_ci				       its_base_time);
1398c2ecf20Sopenharmony_ci		rbt -= its_base_time;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci		time = rbt;
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci		for (i = 0; i < rule->vl.num_entries; i++) {
1448c2ecf20Sopenharmony_ci			u8 gate_state = rule->vl.entries[i].gate_state;
1458c2ecf20Sopenharmony_ci			s64 entry_time = time;
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci			while (entry_time < max_cycle_time) {
1488c2ecf20Sopenharmony_ci				rc = sja1105_insert_gate_entry(gating_cfg, rule,
1498c2ecf20Sopenharmony_ci							       gate_state,
1508c2ecf20Sopenharmony_ci							       entry_time,
1518c2ecf20Sopenharmony_ci							       extack);
1528c2ecf20Sopenharmony_ci				if (rc)
1538c2ecf20Sopenharmony_ci					goto err;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci				entry_time += rule->vl.cycle_time;
1568c2ecf20Sopenharmony_ci			}
1578c2ecf20Sopenharmony_ci			time += rule->vl.entries[i].interval;
1588c2ecf20Sopenharmony_ci		}
1598c2ecf20Sopenharmony_ci	}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	sja1105_gating_cfg_time_to_interval(gating_cfg, max_cycle_time);
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	return 0;
1648c2ecf20Sopenharmony_cierr:
1658c2ecf20Sopenharmony_ci	sja1105_free_gating_config(gating_cfg);
1668c2ecf20Sopenharmony_ci	return rc;
1678c2ecf20Sopenharmony_ci}
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci/* The switch flow classification core implements TTEthernet, which 'thinks' in
1708c2ecf20Sopenharmony_ci * terms of Virtual Links (VL), a concept borrowed from ARINC 664 part 7.
1718c2ecf20Sopenharmony_ci * However it also has one other operating mode (VLLUPFORMAT=0) where it acts
1728c2ecf20Sopenharmony_ci * somewhat closer to a pre-standard implementation of IEEE 802.1Qci
1738c2ecf20Sopenharmony_ci * (Per-Stream Filtering and Policing), which is what the driver is going to be
1748c2ecf20Sopenharmony_ci * implementing.
1758c2ecf20Sopenharmony_ci *
1768c2ecf20Sopenharmony_ci *                                 VL Lookup
1778c2ecf20Sopenharmony_ci *        Key = {DMAC && VLANID   +---------+  Key = { (DMAC[47:16] & VLMASK ==
1788c2ecf20Sopenharmony_ci *               && VLAN PCP      |         |                         VLMARKER)
1798c2ecf20Sopenharmony_ci *               && INGRESS PORT} +---------+                      (both fixed)
1808c2ecf20Sopenharmony_ci *            (exact match,            |             && DMAC[15:0] == VLID
1818c2ecf20Sopenharmony_ci *         all specified in rule)      |                    (specified in rule)
1828c2ecf20Sopenharmony_ci *                                     v             && INGRESS PORT }
1838c2ecf20Sopenharmony_ci *                               ------------
1848c2ecf20Sopenharmony_ci *                    0 (PSFP)  /            \  1 (ARINC664)
1858c2ecf20Sopenharmony_ci *                 +-----------/  VLLUPFORMAT \----------+
1868c2ecf20Sopenharmony_ci *                 |           \    (fixed)   /          |
1878c2ecf20Sopenharmony_ci *                 |            \            /           |
1888c2ecf20Sopenharmony_ci *  0 (forwarding) v             ------------            |
1898c2ecf20Sopenharmony_ci *           ------------                                |
1908c2ecf20Sopenharmony_ci *          /            \  1 (QoS classification)       |
1918c2ecf20Sopenharmony_ci *     +---/  ISCRITICAL  \-----------+                  |
1928c2ecf20Sopenharmony_ci *     |   \  (per rule)  /           |                  |
1938c2ecf20Sopenharmony_ci *     |    \            /   VLID taken from      VLID taken from
1948c2ecf20Sopenharmony_ci *     v     ------------     index of rule       contents of rule
1958c2ecf20Sopenharmony_ci *  select                     that matched         that matched
1968c2ecf20Sopenharmony_ci * DESTPORTS                          |                  |
1978c2ecf20Sopenharmony_ci *  |                                 +---------+--------+
1988c2ecf20Sopenharmony_ci *  |                                           |
1998c2ecf20Sopenharmony_ci *  |                                           v
2008c2ecf20Sopenharmony_ci *  |                                     VL Forwarding
2018c2ecf20Sopenharmony_ci *  |                                   (indexed by VLID)
2028c2ecf20Sopenharmony_ci *  |                                      +---------+
2038c2ecf20Sopenharmony_ci *  |                       +--------------|         |
2048c2ecf20Sopenharmony_ci *  |                       |  select TYPE +---------+
2058c2ecf20Sopenharmony_ci *  |                       v
2068c2ecf20Sopenharmony_ci *  |   0 (rate      ------------    1 (time
2078c2ecf20Sopenharmony_ci *  |  constrained) /            \   triggered)
2088c2ecf20Sopenharmony_ci *  |       +------/     TYPE     \------------+
2098c2ecf20Sopenharmony_ci *  |       |      \  (per VLID)  /            |
2108c2ecf20Sopenharmony_ci *  |       v       \            /             v
2118c2ecf20Sopenharmony_ci *  |  VL Policing   ------------         VL Policing
2128c2ecf20Sopenharmony_ci *  | (indexed by VLID)                (indexed by VLID)
2138c2ecf20Sopenharmony_ci *  |  +---------+                        +---------+
2148c2ecf20Sopenharmony_ci *  |  | TYPE=0  |                        | TYPE=1  |
2158c2ecf20Sopenharmony_ci *  |  +---------+                        +---------+
2168c2ecf20Sopenharmony_ci *  |  select SHARINDX                 select SHARINDX to
2178c2ecf20Sopenharmony_ci *  |  to rate-limit                 re-enter VL Forwarding
2188c2ecf20Sopenharmony_ci *  |  groups of VL's               with new VLID for egress
2198c2ecf20Sopenharmony_ci *  |  to same quota                           |
2208c2ecf20Sopenharmony_ci *  |       |                                  |
2218c2ecf20Sopenharmony_ci *  |  select MAXLEN -> exceed => drop    select MAXLEN -> exceed => drop
2228c2ecf20Sopenharmony_ci *  |       |                                  |
2238c2ecf20Sopenharmony_ci *  |       v                                  v
2248c2ecf20Sopenharmony_ci *  |  VL Forwarding                      VL Forwarding
2258c2ecf20Sopenharmony_ci *  | (indexed by SHARINDX)             (indexed by SHARINDX)
2268c2ecf20Sopenharmony_ci *  |  +---------+                        +---------+
2278c2ecf20Sopenharmony_ci *  |  | TYPE=0  |                        | TYPE=1  |
2288c2ecf20Sopenharmony_ci *  |  +---------+                        +---------+
2298c2ecf20Sopenharmony_ci *  |  select PRIORITY,                 select PRIORITY,
2308c2ecf20Sopenharmony_ci *  | PARTITION, DESTPORTS            PARTITION, DESTPORTS
2318c2ecf20Sopenharmony_ci *  |       |                                  |
2328c2ecf20Sopenharmony_ci *  |       v                                  v
2338c2ecf20Sopenharmony_ci *  |  VL Policing                        VL Policing
2348c2ecf20Sopenharmony_ci *  | (indexed by SHARINDX)           (indexed by SHARINDX)
2358c2ecf20Sopenharmony_ci *  |  +---------+                        +---------+
2368c2ecf20Sopenharmony_ci *  |  | TYPE=0  |                        | TYPE=1  |
2378c2ecf20Sopenharmony_ci *  |  +---------+                        +---------+
2388c2ecf20Sopenharmony_ci *  |       |                                  |
2398c2ecf20Sopenharmony_ci *  |       v                                  |
2408c2ecf20Sopenharmony_ci *  |  select BAG, -> exceed => drop           |
2418c2ecf20Sopenharmony_ci *  |    JITTER                                v
2428c2ecf20Sopenharmony_ci *  |       |             ----------------------------------------------
2438c2ecf20Sopenharmony_ci *  |       |            /    Reception Window is open for this VL      \
2448c2ecf20Sopenharmony_ci *  |       |           /    (the Schedule Table executes an entry i     \
2458c2ecf20Sopenharmony_ci *  |       |          /   M <= i < N, for which these conditions hold):  \ no
2468c2ecf20Sopenharmony_ci *  |       |    +----/                                                    \-+
2478c2ecf20Sopenharmony_ci *  |       |    |yes \       WINST[M] == 1 && WINSTINDEX[M] == VLID       / |
2488c2ecf20Sopenharmony_ci *  |       |    |     \     WINEND[N] == 1 && WINSTINDEX[N] == VLID      /  |
2498c2ecf20Sopenharmony_ci *  |       |    |      \                                                /   |
2508c2ecf20Sopenharmony_ci *  |       |    |       \ (the VL window has opened and not yet closed)/    |
2518c2ecf20Sopenharmony_ci *  |       |    |        ----------------------------------------------     |
2528c2ecf20Sopenharmony_ci *  |       |    v                                                           v
2538c2ecf20Sopenharmony_ci *  |       |  dispatch to DESTPORTS when the Schedule Table               drop
2548c2ecf20Sopenharmony_ci *  |       |  executes an entry i with TXEN == 1 && VLINDEX == i
2558c2ecf20Sopenharmony_ci *  v       v
2568c2ecf20Sopenharmony_ci * dispatch immediately to DESTPORTS
2578c2ecf20Sopenharmony_ci *
2588c2ecf20Sopenharmony_ci * The per-port classification key is always composed of {DMAC, VID, PCP} and
2598c2ecf20Sopenharmony_ci * is non-maskable. This 'looks like' the NULL stream identification function
2608c2ecf20Sopenharmony_ci * from IEEE 802.1CB clause 6, except for the extra VLAN PCP. When the switch
2618c2ecf20Sopenharmony_ci * ports operate as VLAN-unaware, we do allow the user to not specify the VLAN
2628c2ecf20Sopenharmony_ci * ID and PCP, and then the port-based defaults will be used.
2638c2ecf20Sopenharmony_ci *
2648c2ecf20Sopenharmony_ci * In TTEthernet, routing is something that needs to be done manually for each
2658c2ecf20Sopenharmony_ci * Virtual Link. So the flow action must always include one of:
2668c2ecf20Sopenharmony_ci * a. 'redirect', 'trap' or 'drop': select the egress port list
2678c2ecf20Sopenharmony_ci * Additionally, the following actions may be applied on a Virtual Link,
2688c2ecf20Sopenharmony_ci * turning it into 'critical' traffic:
2698c2ecf20Sopenharmony_ci * b. 'police': turn it into a rate-constrained VL, with bandwidth limitation
2708c2ecf20Sopenharmony_ci *    given by the maximum frame length, bandwidth allocation gap (BAG) and
2718c2ecf20Sopenharmony_ci *    maximum jitter.
2728c2ecf20Sopenharmony_ci * c. 'gate': turn it into a time-triggered VL, which can be only be received
2738c2ecf20Sopenharmony_ci *    and forwarded according to a given schedule.
2748c2ecf20Sopenharmony_ci */
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_cistatic bool sja1105_vl_key_lower(struct sja1105_vl_lookup_entry *a,
2778c2ecf20Sopenharmony_ci				 struct sja1105_vl_lookup_entry *b)
2788c2ecf20Sopenharmony_ci{
2798c2ecf20Sopenharmony_ci	if (a->macaddr < b->macaddr)
2808c2ecf20Sopenharmony_ci		return true;
2818c2ecf20Sopenharmony_ci	if (a->macaddr > b->macaddr)
2828c2ecf20Sopenharmony_ci		return false;
2838c2ecf20Sopenharmony_ci	if (a->vlanid < b->vlanid)
2848c2ecf20Sopenharmony_ci		return true;
2858c2ecf20Sopenharmony_ci	if (a->vlanid > b->vlanid)
2868c2ecf20Sopenharmony_ci		return false;
2878c2ecf20Sopenharmony_ci	if (a->port < b->port)
2888c2ecf20Sopenharmony_ci		return true;
2898c2ecf20Sopenharmony_ci	if (a->port > b->port)
2908c2ecf20Sopenharmony_ci		return false;
2918c2ecf20Sopenharmony_ci	if (a->vlanprior < b->vlanprior)
2928c2ecf20Sopenharmony_ci		return true;
2938c2ecf20Sopenharmony_ci	if (a->vlanprior > b->vlanprior)
2948c2ecf20Sopenharmony_ci		return false;
2958c2ecf20Sopenharmony_ci	/* Keys are equal */
2968c2ecf20Sopenharmony_ci	return false;
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_cistatic int sja1105_init_virtual_links(struct sja1105_private *priv,
3008c2ecf20Sopenharmony_ci				      struct netlink_ext_ack *extack)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	struct sja1105_vl_policing_entry *vl_policing;
3038c2ecf20Sopenharmony_ci	struct sja1105_vl_forwarding_entry *vl_fwd;
3048c2ecf20Sopenharmony_ci	struct sja1105_vl_lookup_entry *vl_lookup;
3058c2ecf20Sopenharmony_ci	bool have_critical_virtual_links = false;
3068c2ecf20Sopenharmony_ci	struct sja1105_table *table;
3078c2ecf20Sopenharmony_ci	struct sja1105_rule *rule;
3088c2ecf20Sopenharmony_ci	int num_virtual_links = 0;
3098c2ecf20Sopenharmony_ci	int max_sharindx = 0;
3108c2ecf20Sopenharmony_ci	int i, j, k;
3118c2ecf20Sopenharmony_ci
3128c2ecf20Sopenharmony_ci	/* Figure out the dimensioning of the problem */
3138c2ecf20Sopenharmony_ci	list_for_each_entry(rule, &priv->flow_block.rules, list) {
3148c2ecf20Sopenharmony_ci		if (rule->type != SJA1105_RULE_VL)
3158c2ecf20Sopenharmony_ci			continue;
3168c2ecf20Sopenharmony_ci		/* Each VL lookup entry matches on a single ingress port */
3178c2ecf20Sopenharmony_ci		num_virtual_links += hweight_long(rule->port_mask);
3188c2ecf20Sopenharmony_ci
3198c2ecf20Sopenharmony_ci		if (rule->vl.type != SJA1105_VL_NONCRITICAL)
3208c2ecf20Sopenharmony_ci			have_critical_virtual_links = true;
3218c2ecf20Sopenharmony_ci		if (max_sharindx < rule->vl.sharindx)
3228c2ecf20Sopenharmony_ci			max_sharindx = rule->vl.sharindx;
3238c2ecf20Sopenharmony_ci	}
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	if (num_virtual_links > SJA1105_MAX_VL_LOOKUP_COUNT) {
3268c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Not enough VL entries available");
3278c2ecf20Sopenharmony_ci		return -ENOSPC;
3288c2ecf20Sopenharmony_ci	}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_ci	if (max_sharindx + 1 > SJA1105_MAX_VL_LOOKUP_COUNT) {
3318c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Policer index out of range");
3328c2ecf20Sopenharmony_ci		return -ENOSPC;
3338c2ecf20Sopenharmony_ci	}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	max_sharindx = max_t(int, num_virtual_links, max_sharindx) + 1;
3368c2ecf20Sopenharmony_ci
3378c2ecf20Sopenharmony_ci	/* Discard previous VL Lookup Table */
3388c2ecf20Sopenharmony_ci	table = &priv->static_config.tables[BLK_IDX_VL_LOOKUP];
3398c2ecf20Sopenharmony_ci	if (table->entry_count) {
3408c2ecf20Sopenharmony_ci		kfree(table->entries);
3418c2ecf20Sopenharmony_ci		table->entry_count = 0;
3428c2ecf20Sopenharmony_ci	}
3438c2ecf20Sopenharmony_ci
3448c2ecf20Sopenharmony_ci	/* Discard previous VL Policing Table */
3458c2ecf20Sopenharmony_ci	table = &priv->static_config.tables[BLK_IDX_VL_POLICING];
3468c2ecf20Sopenharmony_ci	if (table->entry_count) {
3478c2ecf20Sopenharmony_ci		kfree(table->entries);
3488c2ecf20Sopenharmony_ci		table->entry_count = 0;
3498c2ecf20Sopenharmony_ci	}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci	/* Discard previous VL Forwarding Table */
3528c2ecf20Sopenharmony_ci	table = &priv->static_config.tables[BLK_IDX_VL_FORWARDING];
3538c2ecf20Sopenharmony_ci	if (table->entry_count) {
3548c2ecf20Sopenharmony_ci		kfree(table->entries);
3558c2ecf20Sopenharmony_ci		table->entry_count = 0;
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	/* Discard previous VL Forwarding Parameters Table */
3598c2ecf20Sopenharmony_ci	table = &priv->static_config.tables[BLK_IDX_VL_FORWARDING_PARAMS];
3608c2ecf20Sopenharmony_ci	if (table->entry_count) {
3618c2ecf20Sopenharmony_ci		kfree(table->entries);
3628c2ecf20Sopenharmony_ci		table->entry_count = 0;
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	/* Nothing to do */
3668c2ecf20Sopenharmony_ci	if (!num_virtual_links)
3678c2ecf20Sopenharmony_ci		return 0;
3688c2ecf20Sopenharmony_ci
3698c2ecf20Sopenharmony_ci	/* Pre-allocate space in the static config tables */
3708c2ecf20Sopenharmony_ci
3718c2ecf20Sopenharmony_ci	/* VL Lookup Table */
3728c2ecf20Sopenharmony_ci	table = &priv->static_config.tables[BLK_IDX_VL_LOOKUP];
3738c2ecf20Sopenharmony_ci	table->entries = kcalloc(num_virtual_links,
3748c2ecf20Sopenharmony_ci				 table->ops->unpacked_entry_size,
3758c2ecf20Sopenharmony_ci				 GFP_KERNEL);
3768c2ecf20Sopenharmony_ci	if (!table->entries)
3778c2ecf20Sopenharmony_ci		return -ENOMEM;
3788c2ecf20Sopenharmony_ci	table->entry_count = num_virtual_links;
3798c2ecf20Sopenharmony_ci	vl_lookup = table->entries;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	k = 0;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	list_for_each_entry(rule, &priv->flow_block.rules, list) {
3848c2ecf20Sopenharmony_ci		unsigned long port;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci		if (rule->type != SJA1105_RULE_VL)
3878c2ecf20Sopenharmony_ci			continue;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci		for_each_set_bit(port, &rule->port_mask, SJA1105_NUM_PORTS) {
3908c2ecf20Sopenharmony_ci			vl_lookup[k].format = SJA1105_VL_FORMAT_PSFP;
3918c2ecf20Sopenharmony_ci			vl_lookup[k].port = port;
3928c2ecf20Sopenharmony_ci			vl_lookup[k].macaddr = rule->key.vl.dmac;
3938c2ecf20Sopenharmony_ci			if (rule->key.type == SJA1105_KEY_VLAN_AWARE_VL) {
3948c2ecf20Sopenharmony_ci				vl_lookup[k].vlanid = rule->key.vl.vid;
3958c2ecf20Sopenharmony_ci				vl_lookup[k].vlanprior = rule->key.vl.pcp;
3968c2ecf20Sopenharmony_ci			} else {
3978c2ecf20Sopenharmony_ci				u16 vid = dsa_8021q_rx_vid(priv->ds, port);
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_ci				vl_lookup[k].vlanid = vid;
4008c2ecf20Sopenharmony_ci				vl_lookup[k].vlanprior = 0;
4018c2ecf20Sopenharmony_ci			}
4028c2ecf20Sopenharmony_ci			/* For critical VLs, the DESTPORTS mask is taken from
4038c2ecf20Sopenharmony_ci			 * the VL Forwarding Table, so no point in putting it
4048c2ecf20Sopenharmony_ci			 * in the VL Lookup Table
4058c2ecf20Sopenharmony_ci			 */
4068c2ecf20Sopenharmony_ci			if (rule->vl.type == SJA1105_VL_NONCRITICAL)
4078c2ecf20Sopenharmony_ci				vl_lookup[k].destports = rule->vl.destports;
4088c2ecf20Sopenharmony_ci			else
4098c2ecf20Sopenharmony_ci				vl_lookup[k].iscritical = true;
4108c2ecf20Sopenharmony_ci			vl_lookup[k].flow_cookie = rule->cookie;
4118c2ecf20Sopenharmony_ci			k++;
4128c2ecf20Sopenharmony_ci		}
4138c2ecf20Sopenharmony_ci	}
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	/* UM10944.pdf chapter 4.2.3 VL Lookup table:
4168c2ecf20Sopenharmony_ci	 * "the entries in the VL Lookup table must be sorted in ascending
4178c2ecf20Sopenharmony_ci	 * order (i.e. the smallest value must be loaded first) according to
4188c2ecf20Sopenharmony_ci	 * the following sort order: MACADDR, VLANID, PORT, VLANPRIOR."
4198c2ecf20Sopenharmony_ci	 */
4208c2ecf20Sopenharmony_ci	for (i = 0; i < num_virtual_links; i++) {
4218c2ecf20Sopenharmony_ci		struct sja1105_vl_lookup_entry *a = &vl_lookup[i];
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci		for (j = i + 1; j < num_virtual_links; j++) {
4248c2ecf20Sopenharmony_ci			struct sja1105_vl_lookup_entry *b = &vl_lookup[j];
4258c2ecf20Sopenharmony_ci
4268c2ecf20Sopenharmony_ci			if (sja1105_vl_key_lower(b, a)) {
4278c2ecf20Sopenharmony_ci				struct sja1105_vl_lookup_entry tmp = *a;
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci				*a = *b;
4308c2ecf20Sopenharmony_ci				*b = tmp;
4318c2ecf20Sopenharmony_ci			}
4328c2ecf20Sopenharmony_ci		}
4338c2ecf20Sopenharmony_ci	}
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	if (!have_critical_virtual_links)
4368c2ecf20Sopenharmony_ci		return 0;
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci	/* VL Policing Table */
4398c2ecf20Sopenharmony_ci	table = &priv->static_config.tables[BLK_IDX_VL_POLICING];
4408c2ecf20Sopenharmony_ci	table->entries = kcalloc(max_sharindx, table->ops->unpacked_entry_size,
4418c2ecf20Sopenharmony_ci				 GFP_KERNEL);
4428c2ecf20Sopenharmony_ci	if (!table->entries)
4438c2ecf20Sopenharmony_ci		return -ENOMEM;
4448c2ecf20Sopenharmony_ci	table->entry_count = max_sharindx;
4458c2ecf20Sopenharmony_ci	vl_policing = table->entries;
4468c2ecf20Sopenharmony_ci
4478c2ecf20Sopenharmony_ci	/* VL Forwarding Table */
4488c2ecf20Sopenharmony_ci	table = &priv->static_config.tables[BLK_IDX_VL_FORWARDING];
4498c2ecf20Sopenharmony_ci	table->entries = kcalloc(max_sharindx, table->ops->unpacked_entry_size,
4508c2ecf20Sopenharmony_ci				 GFP_KERNEL);
4518c2ecf20Sopenharmony_ci	if (!table->entries)
4528c2ecf20Sopenharmony_ci		return -ENOMEM;
4538c2ecf20Sopenharmony_ci	table->entry_count = max_sharindx;
4548c2ecf20Sopenharmony_ci	vl_fwd = table->entries;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	/* VL Forwarding Parameters Table */
4578c2ecf20Sopenharmony_ci	table = &priv->static_config.tables[BLK_IDX_VL_FORWARDING_PARAMS];
4588c2ecf20Sopenharmony_ci	table->entries = kcalloc(1, table->ops->unpacked_entry_size,
4598c2ecf20Sopenharmony_ci				 GFP_KERNEL);
4608c2ecf20Sopenharmony_ci	if (!table->entries)
4618c2ecf20Sopenharmony_ci		return -ENOMEM;
4628c2ecf20Sopenharmony_ci	table->entry_count = 1;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	for (i = 0; i < num_virtual_links; i++) {
4658c2ecf20Sopenharmony_ci		unsigned long cookie = vl_lookup[i].flow_cookie;
4668c2ecf20Sopenharmony_ci		struct sja1105_rule *rule = sja1105_rule_find(priv, cookie);
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci		if (rule->vl.type == SJA1105_VL_NONCRITICAL)
4698c2ecf20Sopenharmony_ci			continue;
4708c2ecf20Sopenharmony_ci		if (rule->vl.type == SJA1105_VL_TIME_TRIGGERED) {
4718c2ecf20Sopenharmony_ci			int sharindx = rule->vl.sharindx;
4728c2ecf20Sopenharmony_ci
4738c2ecf20Sopenharmony_ci			vl_policing[i].type = 1;
4748c2ecf20Sopenharmony_ci			vl_policing[i].sharindx = sharindx;
4758c2ecf20Sopenharmony_ci			vl_policing[i].maxlen = rule->vl.maxlen;
4768c2ecf20Sopenharmony_ci			vl_policing[sharindx].type = 1;
4778c2ecf20Sopenharmony_ci
4788c2ecf20Sopenharmony_ci			vl_fwd[i].type = 1;
4798c2ecf20Sopenharmony_ci			vl_fwd[sharindx].type = 1;
4808c2ecf20Sopenharmony_ci			vl_fwd[sharindx].priority = rule->vl.ipv;
4818c2ecf20Sopenharmony_ci			vl_fwd[sharindx].partition = 0;
4828c2ecf20Sopenharmony_ci			vl_fwd[sharindx].destports = rule->vl.destports;
4838c2ecf20Sopenharmony_ci		}
4848c2ecf20Sopenharmony_ci	}
4858c2ecf20Sopenharmony_ci
4868c2ecf20Sopenharmony_ci	sja1105_frame_memory_partitioning(priv);
4878c2ecf20Sopenharmony_ci
4888c2ecf20Sopenharmony_ci	return 0;
4898c2ecf20Sopenharmony_ci}
4908c2ecf20Sopenharmony_ci
4918c2ecf20Sopenharmony_ciint sja1105_vl_redirect(struct sja1105_private *priv, int port,
4928c2ecf20Sopenharmony_ci			struct netlink_ext_ack *extack, unsigned long cookie,
4938c2ecf20Sopenharmony_ci			struct sja1105_key *key, unsigned long destports,
4948c2ecf20Sopenharmony_ci			bool append)
4958c2ecf20Sopenharmony_ci{
4968c2ecf20Sopenharmony_ci	struct sja1105_rule *rule = sja1105_rule_find(priv, cookie);
4978c2ecf20Sopenharmony_ci	int rc;
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci	if (priv->vlan_state == SJA1105_VLAN_UNAWARE &&
5008c2ecf20Sopenharmony_ci	    key->type != SJA1105_KEY_VLAN_UNAWARE_VL) {
5018c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
5028c2ecf20Sopenharmony_ci				   "Can only redirect based on DMAC");
5038c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
5048c2ecf20Sopenharmony_ci	} else if ((priv->vlan_state == SJA1105_VLAN_BEST_EFFORT ||
5058c2ecf20Sopenharmony_ci		    priv->vlan_state == SJA1105_VLAN_FILTERING_FULL) &&
5068c2ecf20Sopenharmony_ci		   key->type != SJA1105_KEY_VLAN_AWARE_VL) {
5078c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
5088c2ecf20Sopenharmony_ci				   "Can only redirect based on {DMAC, VID, PCP}");
5098c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
5108c2ecf20Sopenharmony_ci	}
5118c2ecf20Sopenharmony_ci
5128c2ecf20Sopenharmony_ci	if (!rule) {
5138c2ecf20Sopenharmony_ci		rule = kzalloc(sizeof(*rule), GFP_KERNEL);
5148c2ecf20Sopenharmony_ci		if (!rule)
5158c2ecf20Sopenharmony_ci			return -ENOMEM;
5168c2ecf20Sopenharmony_ci
5178c2ecf20Sopenharmony_ci		rule->cookie = cookie;
5188c2ecf20Sopenharmony_ci		rule->type = SJA1105_RULE_VL;
5198c2ecf20Sopenharmony_ci		rule->key = *key;
5208c2ecf20Sopenharmony_ci		list_add(&rule->list, &priv->flow_block.rules);
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	rule->port_mask |= BIT(port);
5248c2ecf20Sopenharmony_ci	if (append)
5258c2ecf20Sopenharmony_ci		rule->vl.destports |= destports;
5268c2ecf20Sopenharmony_ci	else
5278c2ecf20Sopenharmony_ci		rule->vl.destports = destports;
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	rc = sja1105_init_virtual_links(priv, extack);
5308c2ecf20Sopenharmony_ci	if (rc) {
5318c2ecf20Sopenharmony_ci		rule->port_mask &= ~BIT(port);
5328c2ecf20Sopenharmony_ci		if (!rule->port_mask) {
5338c2ecf20Sopenharmony_ci			list_del(&rule->list);
5348c2ecf20Sopenharmony_ci			kfree(rule);
5358c2ecf20Sopenharmony_ci		}
5368c2ecf20Sopenharmony_ci	}
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	return rc;
5398c2ecf20Sopenharmony_ci}
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ciint sja1105_vl_delete(struct sja1105_private *priv, int port,
5428c2ecf20Sopenharmony_ci		      struct sja1105_rule *rule, struct netlink_ext_ack *extack)
5438c2ecf20Sopenharmony_ci{
5448c2ecf20Sopenharmony_ci	int rc;
5458c2ecf20Sopenharmony_ci
5468c2ecf20Sopenharmony_ci	rule->port_mask &= ~BIT(port);
5478c2ecf20Sopenharmony_ci	if (!rule->port_mask) {
5488c2ecf20Sopenharmony_ci		list_del(&rule->list);
5498c2ecf20Sopenharmony_ci		kfree(rule);
5508c2ecf20Sopenharmony_ci	}
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	rc = sja1105_compose_gating_subschedule(priv, extack);
5538c2ecf20Sopenharmony_ci	if (rc)
5548c2ecf20Sopenharmony_ci		return rc;
5558c2ecf20Sopenharmony_ci
5568c2ecf20Sopenharmony_ci	rc = sja1105_init_virtual_links(priv, extack);
5578c2ecf20Sopenharmony_ci	if (rc)
5588c2ecf20Sopenharmony_ci		return rc;
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci	rc = sja1105_init_scheduling(priv);
5618c2ecf20Sopenharmony_ci	if (rc < 0)
5628c2ecf20Sopenharmony_ci		return rc;
5638c2ecf20Sopenharmony_ci
5648c2ecf20Sopenharmony_ci	return sja1105_static_config_reload(priv, SJA1105_VIRTUAL_LINKS);
5658c2ecf20Sopenharmony_ci}
5668c2ecf20Sopenharmony_ci
5678c2ecf20Sopenharmony_ciint sja1105_vl_gate(struct sja1105_private *priv, int port,
5688c2ecf20Sopenharmony_ci		    struct netlink_ext_ack *extack, unsigned long cookie,
5698c2ecf20Sopenharmony_ci		    struct sja1105_key *key, u32 index, s32 prio,
5708c2ecf20Sopenharmony_ci		    u64 base_time, u64 cycle_time, u64 cycle_time_ext,
5718c2ecf20Sopenharmony_ci		    u32 num_entries, struct action_gate_entry *entries)
5728c2ecf20Sopenharmony_ci{
5738c2ecf20Sopenharmony_ci	struct sja1105_rule *rule = sja1105_rule_find(priv, cookie);
5748c2ecf20Sopenharmony_ci	int ipv = -1;
5758c2ecf20Sopenharmony_ci	int i, rc;
5768c2ecf20Sopenharmony_ci	s32 rem;
5778c2ecf20Sopenharmony_ci
5788c2ecf20Sopenharmony_ci	if (cycle_time_ext) {
5798c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
5808c2ecf20Sopenharmony_ci				   "Cycle time extension not supported");
5818c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
5828c2ecf20Sopenharmony_ci	}
5838c2ecf20Sopenharmony_ci
5848c2ecf20Sopenharmony_ci	div_s64_rem(base_time, sja1105_delta_to_ns(1), &rem);
5858c2ecf20Sopenharmony_ci	if (rem) {
5868c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
5878c2ecf20Sopenharmony_ci				   "Base time must be multiple of 200 ns");
5888c2ecf20Sopenharmony_ci		return -ERANGE;
5898c2ecf20Sopenharmony_ci	}
5908c2ecf20Sopenharmony_ci
5918c2ecf20Sopenharmony_ci	div_s64_rem(cycle_time, sja1105_delta_to_ns(1), &rem);
5928c2ecf20Sopenharmony_ci	if (rem) {
5938c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
5948c2ecf20Sopenharmony_ci				   "Cycle time must be multiple of 200 ns");
5958c2ecf20Sopenharmony_ci		return -ERANGE;
5968c2ecf20Sopenharmony_ci	}
5978c2ecf20Sopenharmony_ci
5988c2ecf20Sopenharmony_ci	if (priv->vlan_state == SJA1105_VLAN_UNAWARE &&
5998c2ecf20Sopenharmony_ci	    key->type != SJA1105_KEY_VLAN_UNAWARE_VL) {
6008c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
6018c2ecf20Sopenharmony_ci				   "Can only gate based on DMAC");
6028c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
6038c2ecf20Sopenharmony_ci	} else if ((priv->vlan_state == SJA1105_VLAN_BEST_EFFORT ||
6048c2ecf20Sopenharmony_ci		    priv->vlan_state == SJA1105_VLAN_FILTERING_FULL) &&
6058c2ecf20Sopenharmony_ci		   key->type != SJA1105_KEY_VLAN_AWARE_VL) {
6068c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack,
6078c2ecf20Sopenharmony_ci				   "Can only gate based on {DMAC, VID, PCP}");
6088c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
6098c2ecf20Sopenharmony_ci	}
6108c2ecf20Sopenharmony_ci
6118c2ecf20Sopenharmony_ci	if (!rule) {
6128c2ecf20Sopenharmony_ci		rule = kzalloc(sizeof(*rule), GFP_KERNEL);
6138c2ecf20Sopenharmony_ci		if (!rule)
6148c2ecf20Sopenharmony_ci			return -ENOMEM;
6158c2ecf20Sopenharmony_ci
6168c2ecf20Sopenharmony_ci		list_add(&rule->list, &priv->flow_block.rules);
6178c2ecf20Sopenharmony_ci		rule->cookie = cookie;
6188c2ecf20Sopenharmony_ci		rule->type = SJA1105_RULE_VL;
6198c2ecf20Sopenharmony_ci		rule->key = *key;
6208c2ecf20Sopenharmony_ci		rule->vl.type = SJA1105_VL_TIME_TRIGGERED;
6218c2ecf20Sopenharmony_ci		rule->vl.sharindx = index;
6228c2ecf20Sopenharmony_ci		rule->vl.base_time = base_time;
6238c2ecf20Sopenharmony_ci		rule->vl.cycle_time = cycle_time;
6248c2ecf20Sopenharmony_ci		rule->vl.num_entries = num_entries;
6258c2ecf20Sopenharmony_ci		rule->vl.entries = kcalloc(num_entries,
6268c2ecf20Sopenharmony_ci					   sizeof(struct action_gate_entry),
6278c2ecf20Sopenharmony_ci					   GFP_KERNEL);
6288c2ecf20Sopenharmony_ci		if (!rule->vl.entries) {
6298c2ecf20Sopenharmony_ci			rc = -ENOMEM;
6308c2ecf20Sopenharmony_ci			goto out;
6318c2ecf20Sopenharmony_ci		}
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci		for (i = 0; i < num_entries; i++) {
6348c2ecf20Sopenharmony_ci			div_s64_rem(entries[i].interval,
6358c2ecf20Sopenharmony_ci				    sja1105_delta_to_ns(1), &rem);
6368c2ecf20Sopenharmony_ci			if (rem) {
6378c2ecf20Sopenharmony_ci				NL_SET_ERR_MSG_MOD(extack,
6388c2ecf20Sopenharmony_ci						   "Interval must be multiple of 200 ns");
6398c2ecf20Sopenharmony_ci				rc = -ERANGE;
6408c2ecf20Sopenharmony_ci				goto out;
6418c2ecf20Sopenharmony_ci			}
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci			if (!entries[i].interval) {
6448c2ecf20Sopenharmony_ci				NL_SET_ERR_MSG_MOD(extack,
6458c2ecf20Sopenharmony_ci						   "Interval cannot be zero");
6468c2ecf20Sopenharmony_ci				rc = -ERANGE;
6478c2ecf20Sopenharmony_ci				goto out;
6488c2ecf20Sopenharmony_ci			}
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_ci			if (ns_to_sja1105_delta(entries[i].interval) >
6518c2ecf20Sopenharmony_ci			    SJA1105_TAS_MAX_DELTA) {
6528c2ecf20Sopenharmony_ci				NL_SET_ERR_MSG_MOD(extack,
6538c2ecf20Sopenharmony_ci						   "Maximum interval is 52 ms");
6548c2ecf20Sopenharmony_ci				rc = -ERANGE;
6558c2ecf20Sopenharmony_ci				goto out;
6568c2ecf20Sopenharmony_ci			}
6578c2ecf20Sopenharmony_ci
6588c2ecf20Sopenharmony_ci			if (entries[i].maxoctets != -1) {
6598c2ecf20Sopenharmony_ci				NL_SET_ERR_MSG_MOD(extack,
6608c2ecf20Sopenharmony_ci						   "Cannot offload IntervalOctetMax");
6618c2ecf20Sopenharmony_ci				rc = -EOPNOTSUPP;
6628c2ecf20Sopenharmony_ci				goto out;
6638c2ecf20Sopenharmony_ci			}
6648c2ecf20Sopenharmony_ci
6658c2ecf20Sopenharmony_ci			if (ipv == -1) {
6668c2ecf20Sopenharmony_ci				ipv = entries[i].ipv;
6678c2ecf20Sopenharmony_ci			} else if (ipv != entries[i].ipv) {
6688c2ecf20Sopenharmony_ci				NL_SET_ERR_MSG_MOD(extack,
6698c2ecf20Sopenharmony_ci						   "Only support a single IPV per VL");
6708c2ecf20Sopenharmony_ci				rc = -EOPNOTSUPP;
6718c2ecf20Sopenharmony_ci				goto out;
6728c2ecf20Sopenharmony_ci			}
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci			rule->vl.entries[i] = entries[i];
6758c2ecf20Sopenharmony_ci		}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_ci		if (ipv == -1) {
6788c2ecf20Sopenharmony_ci			if (key->type == SJA1105_KEY_VLAN_AWARE_VL)
6798c2ecf20Sopenharmony_ci				ipv = key->vl.pcp;
6808c2ecf20Sopenharmony_ci			else
6818c2ecf20Sopenharmony_ci				ipv = 0;
6828c2ecf20Sopenharmony_ci		}
6838c2ecf20Sopenharmony_ci
6848c2ecf20Sopenharmony_ci		/* TODO: support per-flow MTU */
6858c2ecf20Sopenharmony_ci		rule->vl.maxlen = VLAN_ETH_FRAME_LEN + ETH_FCS_LEN;
6868c2ecf20Sopenharmony_ci		rule->vl.ipv = ipv;
6878c2ecf20Sopenharmony_ci	}
6888c2ecf20Sopenharmony_ci
6898c2ecf20Sopenharmony_ci	rule->port_mask |= BIT(port);
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci	rc = sja1105_compose_gating_subschedule(priv, extack);
6928c2ecf20Sopenharmony_ci	if (rc)
6938c2ecf20Sopenharmony_ci		goto out;
6948c2ecf20Sopenharmony_ci
6958c2ecf20Sopenharmony_ci	rc = sja1105_init_virtual_links(priv, extack);
6968c2ecf20Sopenharmony_ci	if (rc)
6978c2ecf20Sopenharmony_ci		goto out;
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci	if (sja1105_gating_check_conflicts(priv, -1, extack)) {
7008c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "Conflict with tc-taprio schedule");
7018c2ecf20Sopenharmony_ci		rc = -ERANGE;
7028c2ecf20Sopenharmony_ci		goto out;
7038c2ecf20Sopenharmony_ci	}
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ciout:
7068c2ecf20Sopenharmony_ci	if (rc) {
7078c2ecf20Sopenharmony_ci		rule->port_mask &= ~BIT(port);
7088c2ecf20Sopenharmony_ci		if (!rule->port_mask) {
7098c2ecf20Sopenharmony_ci			list_del(&rule->list);
7108c2ecf20Sopenharmony_ci			kfree(rule->vl.entries);
7118c2ecf20Sopenharmony_ci			kfree(rule);
7128c2ecf20Sopenharmony_ci		}
7138c2ecf20Sopenharmony_ci	}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	return rc;
7168c2ecf20Sopenharmony_ci}
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_cistatic int sja1105_find_vlid(struct sja1105_private *priv, int port,
7198c2ecf20Sopenharmony_ci			     struct sja1105_key *key)
7208c2ecf20Sopenharmony_ci{
7218c2ecf20Sopenharmony_ci	struct sja1105_vl_lookup_entry *vl_lookup;
7228c2ecf20Sopenharmony_ci	struct sja1105_table *table;
7238c2ecf20Sopenharmony_ci	int i;
7248c2ecf20Sopenharmony_ci
7258c2ecf20Sopenharmony_ci	if (WARN_ON(key->type != SJA1105_KEY_VLAN_AWARE_VL &&
7268c2ecf20Sopenharmony_ci		    key->type != SJA1105_KEY_VLAN_UNAWARE_VL))
7278c2ecf20Sopenharmony_ci		return -1;
7288c2ecf20Sopenharmony_ci
7298c2ecf20Sopenharmony_ci	table = &priv->static_config.tables[BLK_IDX_VL_LOOKUP];
7308c2ecf20Sopenharmony_ci	vl_lookup = table->entries;
7318c2ecf20Sopenharmony_ci
7328c2ecf20Sopenharmony_ci	for (i = 0; i < table->entry_count; i++) {
7338c2ecf20Sopenharmony_ci		if (key->type == SJA1105_KEY_VLAN_AWARE_VL) {
7348c2ecf20Sopenharmony_ci			if (vl_lookup[i].port == port &&
7358c2ecf20Sopenharmony_ci			    vl_lookup[i].macaddr == key->vl.dmac &&
7368c2ecf20Sopenharmony_ci			    vl_lookup[i].vlanid == key->vl.vid &&
7378c2ecf20Sopenharmony_ci			    vl_lookup[i].vlanprior == key->vl.pcp)
7388c2ecf20Sopenharmony_ci				return i;
7398c2ecf20Sopenharmony_ci		} else {
7408c2ecf20Sopenharmony_ci			if (vl_lookup[i].port == port &&
7418c2ecf20Sopenharmony_ci			    vl_lookup[i].macaddr == key->vl.dmac)
7428c2ecf20Sopenharmony_ci				return i;
7438c2ecf20Sopenharmony_ci		}
7448c2ecf20Sopenharmony_ci	}
7458c2ecf20Sopenharmony_ci
7468c2ecf20Sopenharmony_ci	return -1;
7478c2ecf20Sopenharmony_ci}
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ciint sja1105_vl_stats(struct sja1105_private *priv, int port,
7508c2ecf20Sopenharmony_ci		     struct sja1105_rule *rule, struct flow_stats *stats,
7518c2ecf20Sopenharmony_ci		     struct netlink_ext_ack *extack)
7528c2ecf20Sopenharmony_ci{
7538c2ecf20Sopenharmony_ci	const struct sja1105_regs *regs = priv->info->regs;
7548c2ecf20Sopenharmony_ci	u8 buf[SJA1105_SIZE_VL_STATUS] = {0};
7558c2ecf20Sopenharmony_ci	u64 unreleased;
7568c2ecf20Sopenharmony_ci	u64 timingerr;
7578c2ecf20Sopenharmony_ci	u64 lengtherr;
7588c2ecf20Sopenharmony_ci	int vlid, rc;
7598c2ecf20Sopenharmony_ci	u64 pkts;
7608c2ecf20Sopenharmony_ci
7618c2ecf20Sopenharmony_ci	if (rule->vl.type != SJA1105_VL_TIME_TRIGGERED)
7628c2ecf20Sopenharmony_ci		return 0;
7638c2ecf20Sopenharmony_ci
7648c2ecf20Sopenharmony_ci	vlid = sja1105_find_vlid(priv, port, &rule->key);
7658c2ecf20Sopenharmony_ci	if (vlid < 0)
7668c2ecf20Sopenharmony_ci		return 0;
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	rc = sja1105_xfer_buf(priv, SPI_READ, regs->vl_status + 2 * vlid, buf,
7698c2ecf20Sopenharmony_ci			      SJA1105_SIZE_VL_STATUS);
7708c2ecf20Sopenharmony_ci	if (rc) {
7718c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_MOD(extack, "SPI access failed");
7728c2ecf20Sopenharmony_ci		return rc;
7738c2ecf20Sopenharmony_ci	}
7748c2ecf20Sopenharmony_ci
7758c2ecf20Sopenharmony_ci	sja1105_unpack(buf, &timingerr,  31, 16, SJA1105_SIZE_VL_STATUS);
7768c2ecf20Sopenharmony_ci	sja1105_unpack(buf, &unreleased, 15,  0, SJA1105_SIZE_VL_STATUS);
7778c2ecf20Sopenharmony_ci	sja1105_unpack(buf, &lengtherr,  47, 32, SJA1105_SIZE_VL_STATUS);
7788c2ecf20Sopenharmony_ci
7798c2ecf20Sopenharmony_ci	pkts = timingerr + unreleased + lengtherr;
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_ci	flow_stats_update(stats, 0, pkts - rule->vl.stats.pkts, 0,
7828c2ecf20Sopenharmony_ci			  jiffies - rule->vl.stats.lastused,
7838c2ecf20Sopenharmony_ci			  FLOW_ACTION_HW_STATS_IMMEDIATE);
7848c2ecf20Sopenharmony_ci
7858c2ecf20Sopenharmony_ci	rule->vl.stats.pkts = pkts;
7868c2ecf20Sopenharmony_ci	rule->vl.stats.lastused = jiffies;
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	return 0;
7898c2ecf20Sopenharmony_ci}
790