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