162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright 2020 NXP 362306a36Sopenharmony_ci */ 462306a36Sopenharmony_ci#include <net/tc_act/tc_gate.h> 562306a36Sopenharmony_ci#include <linux/dsa/8021q.h> 662306a36Sopenharmony_ci#include "sja1105_vl.h" 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#define SJA1105_SIZE_VL_STATUS 8 962306a36Sopenharmony_ci 1062306a36Sopenharmony_ci/* Insert into the global gate list, sorted by gate action time. */ 1162306a36Sopenharmony_cistatic int sja1105_insert_gate_entry(struct sja1105_gating_config *gating_cfg, 1262306a36Sopenharmony_ci struct sja1105_rule *rule, 1362306a36Sopenharmony_ci u8 gate_state, s64 entry_time, 1462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 1562306a36Sopenharmony_ci{ 1662306a36Sopenharmony_ci struct sja1105_gate_entry *e; 1762306a36Sopenharmony_ci int rc; 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci e = kzalloc(sizeof(*e), GFP_KERNEL); 2062306a36Sopenharmony_ci if (!e) 2162306a36Sopenharmony_ci return -ENOMEM; 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ci e->rule = rule; 2462306a36Sopenharmony_ci e->gate_state = gate_state; 2562306a36Sopenharmony_ci e->interval = entry_time; 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci if (list_empty(&gating_cfg->entries)) { 2862306a36Sopenharmony_ci list_add(&e->list, &gating_cfg->entries); 2962306a36Sopenharmony_ci } else { 3062306a36Sopenharmony_ci struct sja1105_gate_entry *p; 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci list_for_each_entry(p, &gating_cfg->entries, list) { 3362306a36Sopenharmony_ci if (p->interval == e->interval) { 3462306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 3562306a36Sopenharmony_ci "Gate conflict"); 3662306a36Sopenharmony_ci rc = -EBUSY; 3762306a36Sopenharmony_ci goto err; 3862306a36Sopenharmony_ci } 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (e->interval < p->interval) 4162306a36Sopenharmony_ci break; 4262306a36Sopenharmony_ci } 4362306a36Sopenharmony_ci list_add(&e->list, p->list.prev); 4462306a36Sopenharmony_ci } 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci gating_cfg->num_entries++; 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci return 0; 4962306a36Sopenharmony_cierr: 5062306a36Sopenharmony_ci kfree(e); 5162306a36Sopenharmony_ci return rc; 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_ci/* The gate entries contain absolute times in their e->interval field. Convert 5562306a36Sopenharmony_ci * that to proper intervals (i.e. "0, 5, 10, 15" to "5, 5, 5, 5"). 5662306a36Sopenharmony_ci */ 5762306a36Sopenharmony_cistatic void 5862306a36Sopenharmony_cisja1105_gating_cfg_time_to_interval(struct sja1105_gating_config *gating_cfg, 5962306a36Sopenharmony_ci u64 cycle_time) 6062306a36Sopenharmony_ci{ 6162306a36Sopenharmony_ci struct sja1105_gate_entry *last_e; 6262306a36Sopenharmony_ci struct sja1105_gate_entry *e; 6362306a36Sopenharmony_ci struct list_head *prev; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci list_for_each_entry(e, &gating_cfg->entries, list) { 6662306a36Sopenharmony_ci struct sja1105_gate_entry *p; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci prev = e->list.prev; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci if (prev == &gating_cfg->entries) 7162306a36Sopenharmony_ci continue; 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci p = list_entry(prev, struct sja1105_gate_entry, list); 7462306a36Sopenharmony_ci p->interval = e->interval - p->interval; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci last_e = list_last_entry(&gating_cfg->entries, 7762306a36Sopenharmony_ci struct sja1105_gate_entry, list); 7862306a36Sopenharmony_ci last_e->interval = cycle_time - last_e->interval; 7962306a36Sopenharmony_ci} 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic void sja1105_free_gating_config(struct sja1105_gating_config *gating_cfg) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci struct sja1105_gate_entry *e, *n; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci list_for_each_entry_safe(e, n, &gating_cfg->entries, list) { 8662306a36Sopenharmony_ci list_del(&e->list); 8762306a36Sopenharmony_ci kfree(e); 8862306a36Sopenharmony_ci } 8962306a36Sopenharmony_ci} 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_cistatic int sja1105_compose_gating_subschedule(struct sja1105_private *priv, 9262306a36Sopenharmony_ci struct netlink_ext_ack *extack) 9362306a36Sopenharmony_ci{ 9462306a36Sopenharmony_ci struct sja1105_gating_config *gating_cfg = &priv->tas_data.gating_cfg; 9562306a36Sopenharmony_ci struct sja1105_rule *rule; 9662306a36Sopenharmony_ci s64 max_cycle_time = 0; 9762306a36Sopenharmony_ci s64 its_base_time = 0; 9862306a36Sopenharmony_ci int i, rc = 0; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci sja1105_free_gating_config(gating_cfg); 10162306a36Sopenharmony_ci 10262306a36Sopenharmony_ci list_for_each_entry(rule, &priv->flow_block.rules, list) { 10362306a36Sopenharmony_ci if (rule->type != SJA1105_RULE_VL) 10462306a36Sopenharmony_ci continue; 10562306a36Sopenharmony_ci if (rule->vl.type != SJA1105_VL_TIME_TRIGGERED) 10662306a36Sopenharmony_ci continue; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (max_cycle_time < rule->vl.cycle_time) { 10962306a36Sopenharmony_ci max_cycle_time = rule->vl.cycle_time; 11062306a36Sopenharmony_ci its_base_time = rule->vl.base_time; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (!max_cycle_time) 11562306a36Sopenharmony_ci return 0; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci dev_dbg(priv->ds->dev, "max_cycle_time %lld its_base_time %lld\n", 11862306a36Sopenharmony_ci max_cycle_time, its_base_time); 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci gating_cfg->base_time = its_base_time; 12162306a36Sopenharmony_ci gating_cfg->cycle_time = max_cycle_time; 12262306a36Sopenharmony_ci gating_cfg->num_entries = 0; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci list_for_each_entry(rule, &priv->flow_block.rules, list) { 12562306a36Sopenharmony_ci s64 time; 12662306a36Sopenharmony_ci s64 rbt; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (rule->type != SJA1105_RULE_VL) 12962306a36Sopenharmony_ci continue; 13062306a36Sopenharmony_ci if (rule->vl.type != SJA1105_VL_TIME_TRIGGERED) 13162306a36Sopenharmony_ci continue; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci /* Calculate the difference between this gating schedule's 13462306a36Sopenharmony_ci * base time, and the base time of the gating schedule with the 13562306a36Sopenharmony_ci * longest cycle time. We call it the relative base time (rbt). 13662306a36Sopenharmony_ci */ 13762306a36Sopenharmony_ci rbt = future_base_time(rule->vl.base_time, rule->vl.cycle_time, 13862306a36Sopenharmony_ci its_base_time); 13962306a36Sopenharmony_ci rbt -= its_base_time; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci time = rbt; 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci for (i = 0; i < rule->vl.num_entries; i++) { 14462306a36Sopenharmony_ci u8 gate_state = rule->vl.entries[i].gate_state; 14562306a36Sopenharmony_ci s64 entry_time = time; 14662306a36Sopenharmony_ci 14762306a36Sopenharmony_ci while (entry_time < max_cycle_time) { 14862306a36Sopenharmony_ci rc = sja1105_insert_gate_entry(gating_cfg, rule, 14962306a36Sopenharmony_ci gate_state, 15062306a36Sopenharmony_ci entry_time, 15162306a36Sopenharmony_ci extack); 15262306a36Sopenharmony_ci if (rc) 15362306a36Sopenharmony_ci goto err; 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_ci entry_time += rule->vl.cycle_time; 15662306a36Sopenharmony_ci } 15762306a36Sopenharmony_ci time += rule->vl.entries[i].interval; 15862306a36Sopenharmony_ci } 15962306a36Sopenharmony_ci } 16062306a36Sopenharmony_ci 16162306a36Sopenharmony_ci sja1105_gating_cfg_time_to_interval(gating_cfg, max_cycle_time); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci return 0; 16462306a36Sopenharmony_cierr: 16562306a36Sopenharmony_ci sja1105_free_gating_config(gating_cfg); 16662306a36Sopenharmony_ci return rc; 16762306a36Sopenharmony_ci} 16862306a36Sopenharmony_ci 16962306a36Sopenharmony_ci/* The switch flow classification core implements TTEthernet, which 'thinks' in 17062306a36Sopenharmony_ci * terms of Virtual Links (VL), a concept borrowed from ARINC 664 part 7. 17162306a36Sopenharmony_ci * However it also has one other operating mode (VLLUPFORMAT=0) where it acts 17262306a36Sopenharmony_ci * somewhat closer to a pre-standard implementation of IEEE 802.1Qci 17362306a36Sopenharmony_ci * (Per-Stream Filtering and Policing), which is what the driver is going to be 17462306a36Sopenharmony_ci * implementing. 17562306a36Sopenharmony_ci * 17662306a36Sopenharmony_ci * VL Lookup 17762306a36Sopenharmony_ci * Key = {DMAC && VLANID +---------+ Key = { (DMAC[47:16] & VLMASK == 17862306a36Sopenharmony_ci * && VLAN PCP | | VLMARKER) 17962306a36Sopenharmony_ci * && INGRESS PORT} +---------+ (both fixed) 18062306a36Sopenharmony_ci * (exact match, | && DMAC[15:0] == VLID 18162306a36Sopenharmony_ci * all specified in rule) | (specified in rule) 18262306a36Sopenharmony_ci * v && INGRESS PORT } 18362306a36Sopenharmony_ci * ------------ 18462306a36Sopenharmony_ci * 0 (PSFP) / \ 1 (ARINC664) 18562306a36Sopenharmony_ci * +-----------/ VLLUPFORMAT \----------+ 18662306a36Sopenharmony_ci * | \ (fixed) / | 18762306a36Sopenharmony_ci * | \ / | 18862306a36Sopenharmony_ci * 0 (forwarding) v ------------ | 18962306a36Sopenharmony_ci * ------------ | 19062306a36Sopenharmony_ci * / \ 1 (QoS classification) | 19162306a36Sopenharmony_ci * +---/ ISCRITICAL \-----------+ | 19262306a36Sopenharmony_ci * | \ (per rule) / | | 19362306a36Sopenharmony_ci * | \ / VLID taken from VLID taken from 19462306a36Sopenharmony_ci * v ------------ index of rule contents of rule 19562306a36Sopenharmony_ci * select that matched that matched 19662306a36Sopenharmony_ci * DESTPORTS | | 19762306a36Sopenharmony_ci * | +---------+--------+ 19862306a36Sopenharmony_ci * | | 19962306a36Sopenharmony_ci * | v 20062306a36Sopenharmony_ci * | VL Forwarding 20162306a36Sopenharmony_ci * | (indexed by VLID) 20262306a36Sopenharmony_ci * | +---------+ 20362306a36Sopenharmony_ci * | +--------------| | 20462306a36Sopenharmony_ci * | | select TYPE +---------+ 20562306a36Sopenharmony_ci * | v 20662306a36Sopenharmony_ci * | 0 (rate ------------ 1 (time 20762306a36Sopenharmony_ci * | constrained) / \ triggered) 20862306a36Sopenharmony_ci * | +------/ TYPE \------------+ 20962306a36Sopenharmony_ci * | | \ (per VLID) / | 21062306a36Sopenharmony_ci * | v \ / v 21162306a36Sopenharmony_ci * | VL Policing ------------ VL Policing 21262306a36Sopenharmony_ci * | (indexed by VLID) (indexed by VLID) 21362306a36Sopenharmony_ci * | +---------+ +---------+ 21462306a36Sopenharmony_ci * | | TYPE=0 | | TYPE=1 | 21562306a36Sopenharmony_ci * | +---------+ +---------+ 21662306a36Sopenharmony_ci * | select SHARINDX select SHARINDX to 21762306a36Sopenharmony_ci * | to rate-limit re-enter VL Forwarding 21862306a36Sopenharmony_ci * | groups of VL's with new VLID for egress 21962306a36Sopenharmony_ci * | to same quota | 22062306a36Sopenharmony_ci * | | | 22162306a36Sopenharmony_ci * | select MAXLEN -> exceed => drop select MAXLEN -> exceed => drop 22262306a36Sopenharmony_ci * | | | 22362306a36Sopenharmony_ci * | v v 22462306a36Sopenharmony_ci * | VL Forwarding VL Forwarding 22562306a36Sopenharmony_ci * | (indexed by SHARINDX) (indexed by SHARINDX) 22662306a36Sopenharmony_ci * | +---------+ +---------+ 22762306a36Sopenharmony_ci * | | TYPE=0 | | TYPE=1 | 22862306a36Sopenharmony_ci * | +---------+ +---------+ 22962306a36Sopenharmony_ci * | select PRIORITY, select PRIORITY, 23062306a36Sopenharmony_ci * | PARTITION, DESTPORTS PARTITION, DESTPORTS 23162306a36Sopenharmony_ci * | | | 23262306a36Sopenharmony_ci * | v v 23362306a36Sopenharmony_ci * | VL Policing VL Policing 23462306a36Sopenharmony_ci * | (indexed by SHARINDX) (indexed by SHARINDX) 23562306a36Sopenharmony_ci * | +---------+ +---------+ 23662306a36Sopenharmony_ci * | | TYPE=0 | | TYPE=1 | 23762306a36Sopenharmony_ci * | +---------+ +---------+ 23862306a36Sopenharmony_ci * | | | 23962306a36Sopenharmony_ci * | v | 24062306a36Sopenharmony_ci * | select BAG, -> exceed => drop | 24162306a36Sopenharmony_ci * | JITTER v 24262306a36Sopenharmony_ci * | | ---------------------------------------------- 24362306a36Sopenharmony_ci * | | / Reception Window is open for this VL \ 24462306a36Sopenharmony_ci * | | / (the Schedule Table executes an entry i \ 24562306a36Sopenharmony_ci * | | / M <= i < N, for which these conditions hold): \ no 24662306a36Sopenharmony_ci * | | +----/ \-+ 24762306a36Sopenharmony_ci * | | |yes \ WINST[M] == 1 && WINSTINDEX[M] == VLID / | 24862306a36Sopenharmony_ci * | | | \ WINEND[N] == 1 && WINSTINDEX[N] == VLID / | 24962306a36Sopenharmony_ci * | | | \ / | 25062306a36Sopenharmony_ci * | | | \ (the VL window has opened and not yet closed)/ | 25162306a36Sopenharmony_ci * | | | ---------------------------------------------- | 25262306a36Sopenharmony_ci * | | v v 25362306a36Sopenharmony_ci * | | dispatch to DESTPORTS when the Schedule Table drop 25462306a36Sopenharmony_ci * | | executes an entry i with TXEN == 1 && VLINDEX == i 25562306a36Sopenharmony_ci * v v 25662306a36Sopenharmony_ci * dispatch immediately to DESTPORTS 25762306a36Sopenharmony_ci * 25862306a36Sopenharmony_ci * The per-port classification key is always composed of {DMAC, VID, PCP} and 25962306a36Sopenharmony_ci * is non-maskable. This 'looks like' the NULL stream identification function 26062306a36Sopenharmony_ci * from IEEE 802.1CB clause 6, except for the extra VLAN PCP. When the switch 26162306a36Sopenharmony_ci * ports operate as VLAN-unaware, we do allow the user to not specify the VLAN 26262306a36Sopenharmony_ci * ID and PCP, and then the port-based defaults will be used. 26362306a36Sopenharmony_ci * 26462306a36Sopenharmony_ci * In TTEthernet, routing is something that needs to be done manually for each 26562306a36Sopenharmony_ci * Virtual Link. So the flow action must always include one of: 26662306a36Sopenharmony_ci * a. 'redirect', 'trap' or 'drop': select the egress port list 26762306a36Sopenharmony_ci * Additionally, the following actions may be applied on a Virtual Link, 26862306a36Sopenharmony_ci * turning it into 'critical' traffic: 26962306a36Sopenharmony_ci * b. 'police': turn it into a rate-constrained VL, with bandwidth limitation 27062306a36Sopenharmony_ci * given by the maximum frame length, bandwidth allocation gap (BAG) and 27162306a36Sopenharmony_ci * maximum jitter. 27262306a36Sopenharmony_ci * c. 'gate': turn it into a time-triggered VL, which can be only be received 27362306a36Sopenharmony_ci * and forwarded according to a given schedule. 27462306a36Sopenharmony_ci */ 27562306a36Sopenharmony_ci 27662306a36Sopenharmony_cistatic bool sja1105_vl_key_lower(struct sja1105_vl_lookup_entry *a, 27762306a36Sopenharmony_ci struct sja1105_vl_lookup_entry *b) 27862306a36Sopenharmony_ci{ 27962306a36Sopenharmony_ci if (a->macaddr < b->macaddr) 28062306a36Sopenharmony_ci return true; 28162306a36Sopenharmony_ci if (a->macaddr > b->macaddr) 28262306a36Sopenharmony_ci return false; 28362306a36Sopenharmony_ci if (a->vlanid < b->vlanid) 28462306a36Sopenharmony_ci return true; 28562306a36Sopenharmony_ci if (a->vlanid > b->vlanid) 28662306a36Sopenharmony_ci return false; 28762306a36Sopenharmony_ci if (a->port < b->port) 28862306a36Sopenharmony_ci return true; 28962306a36Sopenharmony_ci if (a->port > b->port) 29062306a36Sopenharmony_ci return false; 29162306a36Sopenharmony_ci if (a->vlanprior < b->vlanprior) 29262306a36Sopenharmony_ci return true; 29362306a36Sopenharmony_ci if (a->vlanprior > b->vlanprior) 29462306a36Sopenharmony_ci return false; 29562306a36Sopenharmony_ci /* Keys are equal */ 29662306a36Sopenharmony_ci return false; 29762306a36Sopenharmony_ci} 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci/* FIXME: this should change when the bridge upper of the port changes. */ 30062306a36Sopenharmony_cistatic u16 sja1105_port_get_tag_8021q_vid(struct dsa_port *dp) 30162306a36Sopenharmony_ci{ 30262306a36Sopenharmony_ci unsigned long bridge_num; 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci if (!dp->bridge) 30562306a36Sopenharmony_ci return dsa_tag_8021q_standalone_vid(dp); 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci bridge_num = dsa_port_bridge_num_get(dp); 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci return dsa_tag_8021q_bridge_vid(bridge_num); 31062306a36Sopenharmony_ci} 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_cistatic int sja1105_init_virtual_links(struct sja1105_private *priv, 31362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 31462306a36Sopenharmony_ci{ 31562306a36Sopenharmony_ci struct sja1105_vl_policing_entry *vl_policing; 31662306a36Sopenharmony_ci struct sja1105_vl_forwarding_entry *vl_fwd; 31762306a36Sopenharmony_ci struct sja1105_vl_lookup_entry *vl_lookup; 31862306a36Sopenharmony_ci bool have_critical_virtual_links = false; 31962306a36Sopenharmony_ci struct sja1105_table *table; 32062306a36Sopenharmony_ci struct sja1105_rule *rule; 32162306a36Sopenharmony_ci int num_virtual_links = 0; 32262306a36Sopenharmony_ci int max_sharindx = 0; 32362306a36Sopenharmony_ci int i, j, k; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* Figure out the dimensioning of the problem */ 32662306a36Sopenharmony_ci list_for_each_entry(rule, &priv->flow_block.rules, list) { 32762306a36Sopenharmony_ci if (rule->type != SJA1105_RULE_VL) 32862306a36Sopenharmony_ci continue; 32962306a36Sopenharmony_ci /* Each VL lookup entry matches on a single ingress port */ 33062306a36Sopenharmony_ci num_virtual_links += hweight_long(rule->port_mask); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if (rule->vl.type != SJA1105_VL_NONCRITICAL) 33362306a36Sopenharmony_ci have_critical_virtual_links = true; 33462306a36Sopenharmony_ci if (max_sharindx < rule->vl.sharindx) 33562306a36Sopenharmony_ci max_sharindx = rule->vl.sharindx; 33662306a36Sopenharmony_ci } 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci if (num_virtual_links > SJA1105_MAX_VL_LOOKUP_COUNT) { 33962306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Not enough VL entries available"); 34062306a36Sopenharmony_ci return -ENOSPC; 34162306a36Sopenharmony_ci } 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci if (max_sharindx + 1 > SJA1105_MAX_VL_LOOKUP_COUNT) { 34462306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Policer index out of range"); 34562306a36Sopenharmony_ci return -ENOSPC; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci max_sharindx = max_t(int, num_virtual_links, max_sharindx) + 1; 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_ci /* Discard previous VL Lookup Table */ 35162306a36Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_VL_LOOKUP]; 35262306a36Sopenharmony_ci if (table->entry_count) { 35362306a36Sopenharmony_ci kfree(table->entries); 35462306a36Sopenharmony_ci table->entry_count = 0; 35562306a36Sopenharmony_ci } 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci /* Discard previous VL Policing Table */ 35862306a36Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_VL_POLICING]; 35962306a36Sopenharmony_ci if (table->entry_count) { 36062306a36Sopenharmony_ci kfree(table->entries); 36162306a36Sopenharmony_ci table->entry_count = 0; 36262306a36Sopenharmony_ci } 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci /* Discard previous VL Forwarding Table */ 36562306a36Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_VL_FORWARDING]; 36662306a36Sopenharmony_ci if (table->entry_count) { 36762306a36Sopenharmony_ci kfree(table->entries); 36862306a36Sopenharmony_ci table->entry_count = 0; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci /* Discard previous VL Forwarding Parameters Table */ 37262306a36Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_VL_FORWARDING_PARAMS]; 37362306a36Sopenharmony_ci if (table->entry_count) { 37462306a36Sopenharmony_ci kfree(table->entries); 37562306a36Sopenharmony_ci table->entry_count = 0; 37662306a36Sopenharmony_ci } 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci /* Nothing to do */ 37962306a36Sopenharmony_ci if (!num_virtual_links) 38062306a36Sopenharmony_ci return 0; 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ci /* Pre-allocate space in the static config tables */ 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* VL Lookup Table */ 38562306a36Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_VL_LOOKUP]; 38662306a36Sopenharmony_ci table->entries = kcalloc(num_virtual_links, 38762306a36Sopenharmony_ci table->ops->unpacked_entry_size, 38862306a36Sopenharmony_ci GFP_KERNEL); 38962306a36Sopenharmony_ci if (!table->entries) 39062306a36Sopenharmony_ci return -ENOMEM; 39162306a36Sopenharmony_ci table->entry_count = num_virtual_links; 39262306a36Sopenharmony_ci vl_lookup = table->entries; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci k = 0; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci list_for_each_entry(rule, &priv->flow_block.rules, list) { 39762306a36Sopenharmony_ci unsigned long port; 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci if (rule->type != SJA1105_RULE_VL) 40062306a36Sopenharmony_ci continue; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci for_each_set_bit(port, &rule->port_mask, SJA1105_MAX_NUM_PORTS) { 40362306a36Sopenharmony_ci vl_lookup[k].format = SJA1105_VL_FORMAT_PSFP; 40462306a36Sopenharmony_ci vl_lookup[k].port = port; 40562306a36Sopenharmony_ci vl_lookup[k].macaddr = rule->key.vl.dmac; 40662306a36Sopenharmony_ci if (rule->key.type == SJA1105_KEY_VLAN_AWARE_VL) { 40762306a36Sopenharmony_ci vl_lookup[k].vlanid = rule->key.vl.vid; 40862306a36Sopenharmony_ci vl_lookup[k].vlanprior = rule->key.vl.pcp; 40962306a36Sopenharmony_ci } else { 41062306a36Sopenharmony_ci /* FIXME */ 41162306a36Sopenharmony_ci struct dsa_port *dp = dsa_to_port(priv->ds, port); 41262306a36Sopenharmony_ci u16 vid = sja1105_port_get_tag_8021q_vid(dp); 41362306a36Sopenharmony_ci 41462306a36Sopenharmony_ci vl_lookup[k].vlanid = vid; 41562306a36Sopenharmony_ci vl_lookup[k].vlanprior = 0; 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci /* For critical VLs, the DESTPORTS mask is taken from 41862306a36Sopenharmony_ci * the VL Forwarding Table, so no point in putting it 41962306a36Sopenharmony_ci * in the VL Lookup Table 42062306a36Sopenharmony_ci */ 42162306a36Sopenharmony_ci if (rule->vl.type == SJA1105_VL_NONCRITICAL) 42262306a36Sopenharmony_ci vl_lookup[k].destports = rule->vl.destports; 42362306a36Sopenharmony_ci else 42462306a36Sopenharmony_ci vl_lookup[k].iscritical = true; 42562306a36Sopenharmony_ci vl_lookup[k].flow_cookie = rule->cookie; 42662306a36Sopenharmony_ci k++; 42762306a36Sopenharmony_ci } 42862306a36Sopenharmony_ci } 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_ci /* UM10944.pdf chapter 4.2.3 VL Lookup table: 43162306a36Sopenharmony_ci * "the entries in the VL Lookup table must be sorted in ascending 43262306a36Sopenharmony_ci * order (i.e. the smallest value must be loaded first) according to 43362306a36Sopenharmony_ci * the following sort order: MACADDR, VLANID, PORT, VLANPRIOR." 43462306a36Sopenharmony_ci */ 43562306a36Sopenharmony_ci for (i = 0; i < num_virtual_links; i++) { 43662306a36Sopenharmony_ci struct sja1105_vl_lookup_entry *a = &vl_lookup[i]; 43762306a36Sopenharmony_ci 43862306a36Sopenharmony_ci for (j = i + 1; j < num_virtual_links; j++) { 43962306a36Sopenharmony_ci struct sja1105_vl_lookup_entry *b = &vl_lookup[j]; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci if (sja1105_vl_key_lower(b, a)) { 44262306a36Sopenharmony_ci struct sja1105_vl_lookup_entry tmp = *a; 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_ci *a = *b; 44562306a36Sopenharmony_ci *b = tmp; 44662306a36Sopenharmony_ci } 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci } 44962306a36Sopenharmony_ci 45062306a36Sopenharmony_ci if (!have_critical_virtual_links) 45162306a36Sopenharmony_ci return 0; 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_ci /* VL Policing Table */ 45462306a36Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_VL_POLICING]; 45562306a36Sopenharmony_ci table->entries = kcalloc(max_sharindx, table->ops->unpacked_entry_size, 45662306a36Sopenharmony_ci GFP_KERNEL); 45762306a36Sopenharmony_ci if (!table->entries) 45862306a36Sopenharmony_ci return -ENOMEM; 45962306a36Sopenharmony_ci table->entry_count = max_sharindx; 46062306a36Sopenharmony_ci vl_policing = table->entries; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* VL Forwarding Table */ 46362306a36Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_VL_FORWARDING]; 46462306a36Sopenharmony_ci table->entries = kcalloc(max_sharindx, table->ops->unpacked_entry_size, 46562306a36Sopenharmony_ci GFP_KERNEL); 46662306a36Sopenharmony_ci if (!table->entries) 46762306a36Sopenharmony_ci return -ENOMEM; 46862306a36Sopenharmony_ci table->entry_count = max_sharindx; 46962306a36Sopenharmony_ci vl_fwd = table->entries; 47062306a36Sopenharmony_ci 47162306a36Sopenharmony_ci /* VL Forwarding Parameters Table */ 47262306a36Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_VL_FORWARDING_PARAMS]; 47362306a36Sopenharmony_ci table->entries = kcalloc(1, table->ops->unpacked_entry_size, 47462306a36Sopenharmony_ci GFP_KERNEL); 47562306a36Sopenharmony_ci if (!table->entries) 47662306a36Sopenharmony_ci return -ENOMEM; 47762306a36Sopenharmony_ci table->entry_count = 1; 47862306a36Sopenharmony_ci 47962306a36Sopenharmony_ci for (i = 0; i < num_virtual_links; i++) { 48062306a36Sopenharmony_ci unsigned long cookie = vl_lookup[i].flow_cookie; 48162306a36Sopenharmony_ci struct sja1105_rule *rule = sja1105_rule_find(priv, cookie); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci if (rule->vl.type == SJA1105_VL_NONCRITICAL) 48462306a36Sopenharmony_ci continue; 48562306a36Sopenharmony_ci if (rule->vl.type == SJA1105_VL_TIME_TRIGGERED) { 48662306a36Sopenharmony_ci int sharindx = rule->vl.sharindx; 48762306a36Sopenharmony_ci 48862306a36Sopenharmony_ci vl_policing[i].type = 1; 48962306a36Sopenharmony_ci vl_policing[i].sharindx = sharindx; 49062306a36Sopenharmony_ci vl_policing[i].maxlen = rule->vl.maxlen; 49162306a36Sopenharmony_ci vl_policing[sharindx].type = 1; 49262306a36Sopenharmony_ci 49362306a36Sopenharmony_ci vl_fwd[i].type = 1; 49462306a36Sopenharmony_ci vl_fwd[sharindx].type = 1; 49562306a36Sopenharmony_ci vl_fwd[sharindx].priority = rule->vl.ipv; 49662306a36Sopenharmony_ci vl_fwd[sharindx].partition = 0; 49762306a36Sopenharmony_ci vl_fwd[sharindx].destports = rule->vl.destports; 49862306a36Sopenharmony_ci } 49962306a36Sopenharmony_ci } 50062306a36Sopenharmony_ci 50162306a36Sopenharmony_ci sja1105_frame_memory_partitioning(priv); 50262306a36Sopenharmony_ci 50362306a36Sopenharmony_ci return 0; 50462306a36Sopenharmony_ci} 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ciint sja1105_vl_redirect(struct sja1105_private *priv, int port, 50762306a36Sopenharmony_ci struct netlink_ext_ack *extack, unsigned long cookie, 50862306a36Sopenharmony_ci struct sja1105_key *key, unsigned long destports, 50962306a36Sopenharmony_ci bool append) 51062306a36Sopenharmony_ci{ 51162306a36Sopenharmony_ci struct sja1105_rule *rule = sja1105_rule_find(priv, cookie); 51262306a36Sopenharmony_ci struct dsa_port *dp = dsa_to_port(priv->ds, port); 51362306a36Sopenharmony_ci bool vlan_aware = dsa_port_is_vlan_filtering(dp); 51462306a36Sopenharmony_ci int rc; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if (!vlan_aware && key->type != SJA1105_KEY_VLAN_UNAWARE_VL) { 51762306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 51862306a36Sopenharmony_ci "Can only redirect based on DMAC"); 51962306a36Sopenharmony_ci return -EOPNOTSUPP; 52062306a36Sopenharmony_ci } else if (vlan_aware && key->type != SJA1105_KEY_VLAN_AWARE_VL) { 52162306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 52262306a36Sopenharmony_ci "Can only redirect based on {DMAC, VID, PCP}"); 52362306a36Sopenharmony_ci return -EOPNOTSUPP; 52462306a36Sopenharmony_ci } 52562306a36Sopenharmony_ci 52662306a36Sopenharmony_ci if (!rule) { 52762306a36Sopenharmony_ci rule = kzalloc(sizeof(*rule), GFP_KERNEL); 52862306a36Sopenharmony_ci if (!rule) 52962306a36Sopenharmony_ci return -ENOMEM; 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci rule->cookie = cookie; 53262306a36Sopenharmony_ci rule->type = SJA1105_RULE_VL; 53362306a36Sopenharmony_ci rule->key = *key; 53462306a36Sopenharmony_ci list_add(&rule->list, &priv->flow_block.rules); 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci rule->port_mask |= BIT(port); 53862306a36Sopenharmony_ci if (append) 53962306a36Sopenharmony_ci rule->vl.destports |= destports; 54062306a36Sopenharmony_ci else 54162306a36Sopenharmony_ci rule->vl.destports = destports; 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci rc = sja1105_init_virtual_links(priv, extack); 54462306a36Sopenharmony_ci if (rc) { 54562306a36Sopenharmony_ci rule->port_mask &= ~BIT(port); 54662306a36Sopenharmony_ci if (!rule->port_mask) { 54762306a36Sopenharmony_ci list_del(&rule->list); 54862306a36Sopenharmony_ci kfree(rule); 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci } 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_ci return rc; 55362306a36Sopenharmony_ci} 55462306a36Sopenharmony_ci 55562306a36Sopenharmony_ciint sja1105_vl_delete(struct sja1105_private *priv, int port, 55662306a36Sopenharmony_ci struct sja1105_rule *rule, struct netlink_ext_ack *extack) 55762306a36Sopenharmony_ci{ 55862306a36Sopenharmony_ci int rc; 55962306a36Sopenharmony_ci 56062306a36Sopenharmony_ci rule->port_mask &= ~BIT(port); 56162306a36Sopenharmony_ci if (!rule->port_mask) { 56262306a36Sopenharmony_ci list_del(&rule->list); 56362306a36Sopenharmony_ci kfree(rule); 56462306a36Sopenharmony_ci } 56562306a36Sopenharmony_ci 56662306a36Sopenharmony_ci rc = sja1105_compose_gating_subschedule(priv, extack); 56762306a36Sopenharmony_ci if (rc) 56862306a36Sopenharmony_ci return rc; 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci rc = sja1105_init_virtual_links(priv, extack); 57162306a36Sopenharmony_ci if (rc) 57262306a36Sopenharmony_ci return rc; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci rc = sja1105_init_scheduling(priv); 57562306a36Sopenharmony_ci if (rc < 0) 57662306a36Sopenharmony_ci return rc; 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci return sja1105_static_config_reload(priv, SJA1105_VIRTUAL_LINKS); 57962306a36Sopenharmony_ci} 58062306a36Sopenharmony_ci 58162306a36Sopenharmony_ciint sja1105_vl_gate(struct sja1105_private *priv, int port, 58262306a36Sopenharmony_ci struct netlink_ext_ack *extack, unsigned long cookie, 58362306a36Sopenharmony_ci struct sja1105_key *key, u32 index, s32 prio, 58462306a36Sopenharmony_ci u64 base_time, u64 cycle_time, u64 cycle_time_ext, 58562306a36Sopenharmony_ci u32 num_entries, struct action_gate_entry *entries) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci struct sja1105_rule *rule = sja1105_rule_find(priv, cookie); 58862306a36Sopenharmony_ci struct dsa_port *dp = dsa_to_port(priv->ds, port); 58962306a36Sopenharmony_ci bool vlan_aware = dsa_port_is_vlan_filtering(dp); 59062306a36Sopenharmony_ci int ipv = -1; 59162306a36Sopenharmony_ci int i, rc; 59262306a36Sopenharmony_ci s32 rem; 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci if (cycle_time_ext) { 59562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 59662306a36Sopenharmony_ci "Cycle time extension not supported"); 59762306a36Sopenharmony_ci return -EOPNOTSUPP; 59862306a36Sopenharmony_ci } 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci div_s64_rem(base_time, sja1105_delta_to_ns(1), &rem); 60162306a36Sopenharmony_ci if (rem) { 60262306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 60362306a36Sopenharmony_ci "Base time must be multiple of 200 ns"); 60462306a36Sopenharmony_ci return -ERANGE; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci div_s64_rem(cycle_time, sja1105_delta_to_ns(1), &rem); 60862306a36Sopenharmony_ci if (rem) { 60962306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 61062306a36Sopenharmony_ci "Cycle time must be multiple of 200 ns"); 61162306a36Sopenharmony_ci return -ERANGE; 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if (!vlan_aware && key->type != SJA1105_KEY_VLAN_UNAWARE_VL) { 61562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 61662306a36Sopenharmony_ci "Can only gate based on DMAC"); 61762306a36Sopenharmony_ci return -EOPNOTSUPP; 61862306a36Sopenharmony_ci } else if (vlan_aware && key->type != SJA1105_KEY_VLAN_AWARE_VL) { 61962306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 62062306a36Sopenharmony_ci "Can only gate based on {DMAC, VID, PCP}"); 62162306a36Sopenharmony_ci return -EOPNOTSUPP; 62262306a36Sopenharmony_ci } 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci if (!rule) { 62562306a36Sopenharmony_ci rule = kzalloc(sizeof(*rule), GFP_KERNEL); 62662306a36Sopenharmony_ci if (!rule) 62762306a36Sopenharmony_ci return -ENOMEM; 62862306a36Sopenharmony_ci 62962306a36Sopenharmony_ci list_add(&rule->list, &priv->flow_block.rules); 63062306a36Sopenharmony_ci rule->cookie = cookie; 63162306a36Sopenharmony_ci rule->type = SJA1105_RULE_VL; 63262306a36Sopenharmony_ci rule->key = *key; 63362306a36Sopenharmony_ci rule->vl.type = SJA1105_VL_TIME_TRIGGERED; 63462306a36Sopenharmony_ci rule->vl.sharindx = index; 63562306a36Sopenharmony_ci rule->vl.base_time = base_time; 63662306a36Sopenharmony_ci rule->vl.cycle_time = cycle_time; 63762306a36Sopenharmony_ci rule->vl.num_entries = num_entries; 63862306a36Sopenharmony_ci rule->vl.entries = kcalloc(num_entries, 63962306a36Sopenharmony_ci sizeof(struct action_gate_entry), 64062306a36Sopenharmony_ci GFP_KERNEL); 64162306a36Sopenharmony_ci if (!rule->vl.entries) { 64262306a36Sopenharmony_ci rc = -ENOMEM; 64362306a36Sopenharmony_ci goto out; 64462306a36Sopenharmony_ci } 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci for (i = 0; i < num_entries; i++) { 64762306a36Sopenharmony_ci div_s64_rem(entries[i].interval, 64862306a36Sopenharmony_ci sja1105_delta_to_ns(1), &rem); 64962306a36Sopenharmony_ci if (rem) { 65062306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 65162306a36Sopenharmony_ci "Interval must be multiple of 200 ns"); 65262306a36Sopenharmony_ci rc = -ERANGE; 65362306a36Sopenharmony_ci goto out; 65462306a36Sopenharmony_ci } 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci if (!entries[i].interval) { 65762306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 65862306a36Sopenharmony_ci "Interval cannot be zero"); 65962306a36Sopenharmony_ci rc = -ERANGE; 66062306a36Sopenharmony_ci goto out; 66162306a36Sopenharmony_ci } 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (ns_to_sja1105_delta(entries[i].interval) > 66462306a36Sopenharmony_ci SJA1105_TAS_MAX_DELTA) { 66562306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 66662306a36Sopenharmony_ci "Maximum interval is 52 ms"); 66762306a36Sopenharmony_ci rc = -ERANGE; 66862306a36Sopenharmony_ci goto out; 66962306a36Sopenharmony_ci } 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_ci if (entries[i].maxoctets != -1) { 67262306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 67362306a36Sopenharmony_ci "Cannot offload IntervalOctetMax"); 67462306a36Sopenharmony_ci rc = -EOPNOTSUPP; 67562306a36Sopenharmony_ci goto out; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci if (ipv == -1) { 67962306a36Sopenharmony_ci ipv = entries[i].ipv; 68062306a36Sopenharmony_ci } else if (ipv != entries[i].ipv) { 68162306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 68262306a36Sopenharmony_ci "Only support a single IPV per VL"); 68362306a36Sopenharmony_ci rc = -EOPNOTSUPP; 68462306a36Sopenharmony_ci goto out; 68562306a36Sopenharmony_ci } 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci rule->vl.entries[i] = entries[i]; 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci if (ipv == -1) { 69162306a36Sopenharmony_ci if (key->type == SJA1105_KEY_VLAN_AWARE_VL) 69262306a36Sopenharmony_ci ipv = key->vl.pcp; 69362306a36Sopenharmony_ci else 69462306a36Sopenharmony_ci ipv = 0; 69562306a36Sopenharmony_ci } 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci /* TODO: support per-flow MTU */ 69862306a36Sopenharmony_ci rule->vl.maxlen = VLAN_ETH_FRAME_LEN + ETH_FCS_LEN; 69962306a36Sopenharmony_ci rule->vl.ipv = ipv; 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci rule->port_mask |= BIT(port); 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci rc = sja1105_compose_gating_subschedule(priv, extack); 70562306a36Sopenharmony_ci if (rc) 70662306a36Sopenharmony_ci goto out; 70762306a36Sopenharmony_ci 70862306a36Sopenharmony_ci rc = sja1105_init_virtual_links(priv, extack); 70962306a36Sopenharmony_ci if (rc) 71062306a36Sopenharmony_ci goto out; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci if (sja1105_gating_check_conflicts(priv, -1, extack)) { 71362306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "Conflict with tc-taprio schedule"); 71462306a36Sopenharmony_ci rc = -ERANGE; 71562306a36Sopenharmony_ci goto out; 71662306a36Sopenharmony_ci } 71762306a36Sopenharmony_ci 71862306a36Sopenharmony_ciout: 71962306a36Sopenharmony_ci if (rc) { 72062306a36Sopenharmony_ci rule->port_mask &= ~BIT(port); 72162306a36Sopenharmony_ci if (!rule->port_mask) { 72262306a36Sopenharmony_ci list_del(&rule->list); 72362306a36Sopenharmony_ci kfree(rule->vl.entries); 72462306a36Sopenharmony_ci kfree(rule); 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci return rc; 72962306a36Sopenharmony_ci} 73062306a36Sopenharmony_ci 73162306a36Sopenharmony_cistatic int sja1105_find_vlid(struct sja1105_private *priv, int port, 73262306a36Sopenharmony_ci struct sja1105_key *key) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci struct sja1105_vl_lookup_entry *vl_lookup; 73562306a36Sopenharmony_ci struct sja1105_table *table; 73662306a36Sopenharmony_ci int i; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci if (WARN_ON(key->type != SJA1105_KEY_VLAN_AWARE_VL && 73962306a36Sopenharmony_ci key->type != SJA1105_KEY_VLAN_UNAWARE_VL)) 74062306a36Sopenharmony_ci return -1; 74162306a36Sopenharmony_ci 74262306a36Sopenharmony_ci table = &priv->static_config.tables[BLK_IDX_VL_LOOKUP]; 74362306a36Sopenharmony_ci vl_lookup = table->entries; 74462306a36Sopenharmony_ci 74562306a36Sopenharmony_ci for (i = 0; i < table->entry_count; i++) { 74662306a36Sopenharmony_ci if (key->type == SJA1105_KEY_VLAN_AWARE_VL) { 74762306a36Sopenharmony_ci if (vl_lookup[i].port == port && 74862306a36Sopenharmony_ci vl_lookup[i].macaddr == key->vl.dmac && 74962306a36Sopenharmony_ci vl_lookup[i].vlanid == key->vl.vid && 75062306a36Sopenharmony_ci vl_lookup[i].vlanprior == key->vl.pcp) 75162306a36Sopenharmony_ci return i; 75262306a36Sopenharmony_ci } else { 75362306a36Sopenharmony_ci if (vl_lookup[i].port == port && 75462306a36Sopenharmony_ci vl_lookup[i].macaddr == key->vl.dmac) 75562306a36Sopenharmony_ci return i; 75662306a36Sopenharmony_ci } 75762306a36Sopenharmony_ci } 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci return -1; 76062306a36Sopenharmony_ci} 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ciint sja1105_vl_stats(struct sja1105_private *priv, int port, 76362306a36Sopenharmony_ci struct sja1105_rule *rule, struct flow_stats *stats, 76462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 76562306a36Sopenharmony_ci{ 76662306a36Sopenharmony_ci const struct sja1105_regs *regs = priv->info->regs; 76762306a36Sopenharmony_ci u8 buf[SJA1105_SIZE_VL_STATUS] = {0}; 76862306a36Sopenharmony_ci u64 unreleased; 76962306a36Sopenharmony_ci u64 timingerr; 77062306a36Sopenharmony_ci u64 lengtherr; 77162306a36Sopenharmony_ci int vlid, rc; 77262306a36Sopenharmony_ci u64 pkts; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci if (rule->vl.type != SJA1105_VL_TIME_TRIGGERED) 77562306a36Sopenharmony_ci return 0; 77662306a36Sopenharmony_ci 77762306a36Sopenharmony_ci vlid = sja1105_find_vlid(priv, port, &rule->key); 77862306a36Sopenharmony_ci if (vlid < 0) 77962306a36Sopenharmony_ci return 0; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci rc = sja1105_xfer_buf(priv, SPI_READ, regs->vl_status + 2 * vlid, buf, 78262306a36Sopenharmony_ci SJA1105_SIZE_VL_STATUS); 78362306a36Sopenharmony_ci if (rc) { 78462306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, "SPI access failed"); 78562306a36Sopenharmony_ci return rc; 78662306a36Sopenharmony_ci } 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci sja1105_unpack(buf, &timingerr, 31, 16, SJA1105_SIZE_VL_STATUS); 78962306a36Sopenharmony_ci sja1105_unpack(buf, &unreleased, 15, 0, SJA1105_SIZE_VL_STATUS); 79062306a36Sopenharmony_ci sja1105_unpack(buf, &lengtherr, 47, 32, SJA1105_SIZE_VL_STATUS); 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci pkts = timingerr + unreleased + lengtherr; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci flow_stats_update(stats, 0, pkts - rule->vl.stats.pkts, 0, 79562306a36Sopenharmony_ci jiffies - rule->vl.stats.lastused, 79662306a36Sopenharmony_ci FLOW_ACTION_HW_STATS_IMMEDIATE); 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci rule->vl.stats.pkts = pkts; 79962306a36Sopenharmony_ci rule->vl.stats.lastused = jiffies; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci return 0; 80262306a36Sopenharmony_ci} 803