162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* Copyright 2019-2021 NXP 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * This is an umbrella module for all network switches that are 562306a36Sopenharmony_ci * register-compatible with Ocelot and that perform I/O to their host CPU 662306a36Sopenharmony_ci * through an NPI (Node Processor Interface) Ethernet port. 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci#include <uapi/linux/if_bridge.h> 962306a36Sopenharmony_ci#include <soc/mscc/ocelot_vcap.h> 1062306a36Sopenharmony_ci#include <soc/mscc/ocelot_qsys.h> 1162306a36Sopenharmony_ci#include <soc/mscc/ocelot_sys.h> 1262306a36Sopenharmony_ci#include <soc/mscc/ocelot_dev.h> 1362306a36Sopenharmony_ci#include <soc/mscc/ocelot_ana.h> 1462306a36Sopenharmony_ci#include <soc/mscc/ocelot_ptp.h> 1562306a36Sopenharmony_ci#include <soc/mscc/ocelot.h> 1662306a36Sopenharmony_ci#include <linux/dsa/8021q.h> 1762306a36Sopenharmony_ci#include <linux/dsa/ocelot.h> 1862306a36Sopenharmony_ci#include <linux/platform_device.h> 1962306a36Sopenharmony_ci#include <linux/ptp_classify.h> 2062306a36Sopenharmony_ci#include <linux/module.h> 2162306a36Sopenharmony_ci#include <linux/of_net.h> 2262306a36Sopenharmony_ci#include <linux/pci.h> 2362306a36Sopenharmony_ci#include <linux/of.h> 2462306a36Sopenharmony_ci#include <net/pkt_sched.h> 2562306a36Sopenharmony_ci#include <net/dsa.h> 2662306a36Sopenharmony_ci#include "felix.h" 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* Translate the DSA database API into the ocelot switch library API, 2962306a36Sopenharmony_ci * which uses VID 0 for all ports that aren't part of a bridge, 3062306a36Sopenharmony_ci * and expects the bridge_dev to be NULL in that case. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_cistatic struct net_device *felix_classify_db(struct dsa_db db) 3362306a36Sopenharmony_ci{ 3462306a36Sopenharmony_ci switch (db.type) { 3562306a36Sopenharmony_ci case DSA_DB_PORT: 3662306a36Sopenharmony_ci case DSA_DB_LAG: 3762306a36Sopenharmony_ci return NULL; 3862306a36Sopenharmony_ci case DSA_DB_BRIDGE: 3962306a36Sopenharmony_ci return db.bridge.dev; 4062306a36Sopenharmony_ci default: 4162306a36Sopenharmony_ci return ERR_PTR(-EOPNOTSUPP); 4262306a36Sopenharmony_ci } 4362306a36Sopenharmony_ci} 4462306a36Sopenharmony_ci 4562306a36Sopenharmony_cistatic int felix_cpu_port_for_master(struct dsa_switch *ds, 4662306a36Sopenharmony_ci struct net_device *master) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 4962306a36Sopenharmony_ci struct dsa_port *cpu_dp; 5062306a36Sopenharmony_ci int lag; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci if (netif_is_lag_master(master)) { 5362306a36Sopenharmony_ci mutex_lock(&ocelot->fwd_domain_lock); 5462306a36Sopenharmony_ci lag = ocelot_bond_get_id(ocelot, master); 5562306a36Sopenharmony_ci mutex_unlock(&ocelot->fwd_domain_lock); 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci return lag; 5862306a36Sopenharmony_ci } 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci cpu_dp = master->dsa_ptr; 6162306a36Sopenharmony_ci return cpu_dp->index; 6262306a36Sopenharmony_ci} 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* Set up VCAP ES0 rules for pushing a tag_8021q VLAN towards the CPU such that 6562306a36Sopenharmony_ci * the tagger can perform RX source port identification. 6662306a36Sopenharmony_ci */ 6762306a36Sopenharmony_cistatic int felix_tag_8021q_vlan_add_rx(struct dsa_switch *ds, int port, 6862306a36Sopenharmony_ci int upstream, u16 vid) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci struct ocelot_vcap_filter *outer_tagging_rule; 7162306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 7262306a36Sopenharmony_ci unsigned long cookie; 7362306a36Sopenharmony_ci int key_length, err; 7462306a36Sopenharmony_ci 7562306a36Sopenharmony_ci key_length = ocelot->vcap[VCAP_ES0].keys[VCAP_ES0_IGR_PORT].length; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci outer_tagging_rule = kzalloc(sizeof(struct ocelot_vcap_filter), 7862306a36Sopenharmony_ci GFP_KERNEL); 7962306a36Sopenharmony_ci if (!outer_tagging_rule) 8062306a36Sopenharmony_ci return -ENOMEM; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci cookie = OCELOT_VCAP_ES0_TAG_8021Q_RXVLAN(ocelot, port, upstream); 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci outer_tagging_rule->key_type = OCELOT_VCAP_KEY_ANY; 8562306a36Sopenharmony_ci outer_tagging_rule->prio = 1; 8662306a36Sopenharmony_ci outer_tagging_rule->id.cookie = cookie; 8762306a36Sopenharmony_ci outer_tagging_rule->id.tc_offload = false; 8862306a36Sopenharmony_ci outer_tagging_rule->block_id = VCAP_ES0; 8962306a36Sopenharmony_ci outer_tagging_rule->type = OCELOT_VCAP_FILTER_OFFLOAD; 9062306a36Sopenharmony_ci outer_tagging_rule->lookup = 0; 9162306a36Sopenharmony_ci outer_tagging_rule->ingress_port.value = port; 9262306a36Sopenharmony_ci outer_tagging_rule->ingress_port.mask = GENMASK(key_length - 1, 0); 9362306a36Sopenharmony_ci outer_tagging_rule->egress_port.value = upstream; 9462306a36Sopenharmony_ci outer_tagging_rule->egress_port.mask = GENMASK(key_length - 1, 0); 9562306a36Sopenharmony_ci outer_tagging_rule->action.push_outer_tag = OCELOT_ES0_TAG; 9662306a36Sopenharmony_ci outer_tagging_rule->action.tag_a_tpid_sel = OCELOT_TAG_TPID_SEL_8021AD; 9762306a36Sopenharmony_ci outer_tagging_rule->action.tag_a_vid_sel = 1; 9862306a36Sopenharmony_ci outer_tagging_rule->action.vid_a_val = vid; 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci err = ocelot_vcap_filter_add(ocelot, outer_tagging_rule, NULL); 10162306a36Sopenharmony_ci if (err) 10262306a36Sopenharmony_ci kfree(outer_tagging_rule); 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci return err; 10562306a36Sopenharmony_ci} 10662306a36Sopenharmony_ci 10762306a36Sopenharmony_cistatic int felix_tag_8021q_vlan_del_rx(struct dsa_switch *ds, int port, 10862306a36Sopenharmony_ci int upstream, u16 vid) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci struct ocelot_vcap_filter *outer_tagging_rule; 11162306a36Sopenharmony_ci struct ocelot_vcap_block *block_vcap_es0; 11262306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 11362306a36Sopenharmony_ci unsigned long cookie; 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci block_vcap_es0 = &ocelot->block[VCAP_ES0]; 11662306a36Sopenharmony_ci cookie = OCELOT_VCAP_ES0_TAG_8021Q_RXVLAN(ocelot, port, upstream); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci outer_tagging_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_es0, 11962306a36Sopenharmony_ci cookie, false); 12062306a36Sopenharmony_ci if (!outer_tagging_rule) 12162306a36Sopenharmony_ci return -ENOENT; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci return ocelot_vcap_filter_del(ocelot, outer_tagging_rule); 12462306a36Sopenharmony_ci} 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_ci/* Set up VCAP IS1 rules for stripping the tag_8021q VLAN on TX and VCAP IS2 12762306a36Sopenharmony_ci * rules for steering those tagged packets towards the correct destination port 12862306a36Sopenharmony_ci */ 12962306a36Sopenharmony_cistatic int felix_tag_8021q_vlan_add_tx(struct dsa_switch *ds, int port, 13062306a36Sopenharmony_ci u16 vid) 13162306a36Sopenharmony_ci{ 13262306a36Sopenharmony_ci struct ocelot_vcap_filter *untagging_rule, *redirect_rule; 13362306a36Sopenharmony_ci unsigned long cpu_ports = dsa_cpu_ports(ds); 13462306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 13562306a36Sopenharmony_ci unsigned long cookie; 13662306a36Sopenharmony_ci int err; 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci untagging_rule = kzalloc(sizeof(struct ocelot_vcap_filter), GFP_KERNEL); 13962306a36Sopenharmony_ci if (!untagging_rule) 14062306a36Sopenharmony_ci return -ENOMEM; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci redirect_rule = kzalloc(sizeof(struct ocelot_vcap_filter), GFP_KERNEL); 14362306a36Sopenharmony_ci if (!redirect_rule) { 14462306a36Sopenharmony_ci kfree(untagging_rule); 14562306a36Sopenharmony_ci return -ENOMEM; 14662306a36Sopenharmony_ci } 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci cookie = OCELOT_VCAP_IS1_TAG_8021Q_TXVLAN(ocelot, port); 14962306a36Sopenharmony_ci 15062306a36Sopenharmony_ci untagging_rule->key_type = OCELOT_VCAP_KEY_ANY; 15162306a36Sopenharmony_ci untagging_rule->ingress_port_mask = cpu_ports; 15262306a36Sopenharmony_ci untagging_rule->vlan.vid.value = vid; 15362306a36Sopenharmony_ci untagging_rule->vlan.vid.mask = VLAN_VID_MASK; 15462306a36Sopenharmony_ci untagging_rule->prio = 1; 15562306a36Sopenharmony_ci untagging_rule->id.cookie = cookie; 15662306a36Sopenharmony_ci untagging_rule->id.tc_offload = false; 15762306a36Sopenharmony_ci untagging_rule->block_id = VCAP_IS1; 15862306a36Sopenharmony_ci untagging_rule->type = OCELOT_VCAP_FILTER_OFFLOAD; 15962306a36Sopenharmony_ci untagging_rule->lookup = 0; 16062306a36Sopenharmony_ci untagging_rule->action.vlan_pop_cnt_ena = true; 16162306a36Sopenharmony_ci untagging_rule->action.vlan_pop_cnt = 1; 16262306a36Sopenharmony_ci untagging_rule->action.pag_override_mask = 0xff; 16362306a36Sopenharmony_ci untagging_rule->action.pag_val = port; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci err = ocelot_vcap_filter_add(ocelot, untagging_rule, NULL); 16662306a36Sopenharmony_ci if (err) { 16762306a36Sopenharmony_ci kfree(untagging_rule); 16862306a36Sopenharmony_ci kfree(redirect_rule); 16962306a36Sopenharmony_ci return err; 17062306a36Sopenharmony_ci } 17162306a36Sopenharmony_ci 17262306a36Sopenharmony_ci cookie = OCELOT_VCAP_IS2_TAG_8021Q_TXVLAN(ocelot, port); 17362306a36Sopenharmony_ci 17462306a36Sopenharmony_ci redirect_rule->key_type = OCELOT_VCAP_KEY_ANY; 17562306a36Sopenharmony_ci redirect_rule->ingress_port_mask = cpu_ports; 17662306a36Sopenharmony_ci redirect_rule->pag = port; 17762306a36Sopenharmony_ci redirect_rule->prio = 1; 17862306a36Sopenharmony_ci redirect_rule->id.cookie = cookie; 17962306a36Sopenharmony_ci redirect_rule->id.tc_offload = false; 18062306a36Sopenharmony_ci redirect_rule->block_id = VCAP_IS2; 18162306a36Sopenharmony_ci redirect_rule->type = OCELOT_VCAP_FILTER_OFFLOAD; 18262306a36Sopenharmony_ci redirect_rule->lookup = 0; 18362306a36Sopenharmony_ci redirect_rule->action.mask_mode = OCELOT_MASK_MODE_REDIRECT; 18462306a36Sopenharmony_ci redirect_rule->action.port_mask = BIT(port); 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci err = ocelot_vcap_filter_add(ocelot, redirect_rule, NULL); 18762306a36Sopenharmony_ci if (err) { 18862306a36Sopenharmony_ci ocelot_vcap_filter_del(ocelot, untagging_rule); 18962306a36Sopenharmony_ci kfree(redirect_rule); 19062306a36Sopenharmony_ci return err; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci 19362306a36Sopenharmony_ci return 0; 19462306a36Sopenharmony_ci} 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_cistatic int felix_tag_8021q_vlan_del_tx(struct dsa_switch *ds, int port, u16 vid) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci struct ocelot_vcap_filter *untagging_rule, *redirect_rule; 19962306a36Sopenharmony_ci struct ocelot_vcap_block *block_vcap_is1; 20062306a36Sopenharmony_ci struct ocelot_vcap_block *block_vcap_is2; 20162306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 20262306a36Sopenharmony_ci unsigned long cookie; 20362306a36Sopenharmony_ci int err; 20462306a36Sopenharmony_ci 20562306a36Sopenharmony_ci block_vcap_is1 = &ocelot->block[VCAP_IS1]; 20662306a36Sopenharmony_ci block_vcap_is2 = &ocelot->block[VCAP_IS2]; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci cookie = OCELOT_VCAP_IS1_TAG_8021Q_TXVLAN(ocelot, port); 20962306a36Sopenharmony_ci untagging_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_is1, 21062306a36Sopenharmony_ci cookie, false); 21162306a36Sopenharmony_ci if (!untagging_rule) 21262306a36Sopenharmony_ci return -ENOENT; 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci err = ocelot_vcap_filter_del(ocelot, untagging_rule); 21562306a36Sopenharmony_ci if (err) 21662306a36Sopenharmony_ci return err; 21762306a36Sopenharmony_ci 21862306a36Sopenharmony_ci cookie = OCELOT_VCAP_IS2_TAG_8021Q_TXVLAN(ocelot, port); 21962306a36Sopenharmony_ci redirect_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_is2, 22062306a36Sopenharmony_ci cookie, false); 22162306a36Sopenharmony_ci if (!redirect_rule) 22262306a36Sopenharmony_ci return -ENOENT; 22362306a36Sopenharmony_ci 22462306a36Sopenharmony_ci return ocelot_vcap_filter_del(ocelot, redirect_rule); 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic int felix_tag_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid, 22862306a36Sopenharmony_ci u16 flags) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci struct dsa_port *cpu_dp; 23162306a36Sopenharmony_ci int err; 23262306a36Sopenharmony_ci 23362306a36Sopenharmony_ci /* tag_8021q.c assumes we are implementing this via port VLAN 23462306a36Sopenharmony_ci * membership, which we aren't. So we don't need to add any VCAP filter 23562306a36Sopenharmony_ci * for the CPU port. 23662306a36Sopenharmony_ci */ 23762306a36Sopenharmony_ci if (!dsa_is_user_port(ds, port)) 23862306a36Sopenharmony_ci return 0; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci dsa_switch_for_each_cpu_port(cpu_dp, ds) { 24162306a36Sopenharmony_ci err = felix_tag_8021q_vlan_add_rx(ds, port, cpu_dp->index, vid); 24262306a36Sopenharmony_ci if (err) 24362306a36Sopenharmony_ci return err; 24462306a36Sopenharmony_ci } 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_ci err = felix_tag_8021q_vlan_add_tx(ds, port, vid); 24762306a36Sopenharmony_ci if (err) 24862306a36Sopenharmony_ci goto add_tx_failed; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci return 0; 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ciadd_tx_failed: 25362306a36Sopenharmony_ci dsa_switch_for_each_cpu_port(cpu_dp, ds) 25462306a36Sopenharmony_ci felix_tag_8021q_vlan_del_rx(ds, port, cpu_dp->index, vid); 25562306a36Sopenharmony_ci 25662306a36Sopenharmony_ci return err; 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic int felix_tag_8021q_vlan_del(struct dsa_switch *ds, int port, u16 vid) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci struct dsa_port *cpu_dp; 26262306a36Sopenharmony_ci int err; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (!dsa_is_user_port(ds, port)) 26562306a36Sopenharmony_ci return 0; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci dsa_switch_for_each_cpu_port(cpu_dp, ds) { 26862306a36Sopenharmony_ci err = felix_tag_8021q_vlan_del_rx(ds, port, cpu_dp->index, vid); 26962306a36Sopenharmony_ci if (err) 27062306a36Sopenharmony_ci return err; 27162306a36Sopenharmony_ci } 27262306a36Sopenharmony_ci 27362306a36Sopenharmony_ci err = felix_tag_8021q_vlan_del_tx(ds, port, vid); 27462306a36Sopenharmony_ci if (err) 27562306a36Sopenharmony_ci goto del_tx_failed; 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci return 0; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_cidel_tx_failed: 28062306a36Sopenharmony_ci dsa_switch_for_each_cpu_port(cpu_dp, ds) 28162306a36Sopenharmony_ci felix_tag_8021q_vlan_add_rx(ds, port, cpu_dp->index, vid); 28262306a36Sopenharmony_ci 28362306a36Sopenharmony_ci return err; 28462306a36Sopenharmony_ci} 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_cistatic int felix_trap_get_cpu_port(struct dsa_switch *ds, 28762306a36Sopenharmony_ci const struct ocelot_vcap_filter *trap) 28862306a36Sopenharmony_ci{ 28962306a36Sopenharmony_ci struct dsa_port *dp; 29062306a36Sopenharmony_ci int first_port; 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_ci if (WARN_ON(!trap->ingress_port_mask)) 29362306a36Sopenharmony_ci return -1; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci first_port = __ffs(trap->ingress_port_mask); 29662306a36Sopenharmony_ci dp = dsa_to_port(ds, first_port); 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci return dp->cpu_dp->index; 29962306a36Sopenharmony_ci} 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci/* On switches with no extraction IRQ wired, trapped packets need to be 30262306a36Sopenharmony_ci * replicated over Ethernet as well, otherwise we'd get no notification of 30362306a36Sopenharmony_ci * their arrival when using the ocelot-8021q tagging protocol. 30462306a36Sopenharmony_ci */ 30562306a36Sopenharmony_cistatic int felix_update_trapping_destinations(struct dsa_switch *ds, 30662306a36Sopenharmony_ci bool using_tag_8021q) 30762306a36Sopenharmony_ci{ 30862306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 30962306a36Sopenharmony_ci struct felix *felix = ocelot_to_felix(ocelot); 31062306a36Sopenharmony_ci struct ocelot_vcap_block *block_vcap_is2; 31162306a36Sopenharmony_ci struct ocelot_vcap_filter *trap; 31262306a36Sopenharmony_ci enum ocelot_mask_mode mask_mode; 31362306a36Sopenharmony_ci unsigned long port_mask; 31462306a36Sopenharmony_ci bool cpu_copy_ena; 31562306a36Sopenharmony_ci int err; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci if (!felix->info->quirk_no_xtr_irq) 31862306a36Sopenharmony_ci return 0; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci /* We are sure that "cpu" was found, otherwise 32162306a36Sopenharmony_ci * dsa_tree_setup_default_cpu() would have failed earlier. 32262306a36Sopenharmony_ci */ 32362306a36Sopenharmony_ci block_vcap_is2 = &ocelot->block[VCAP_IS2]; 32462306a36Sopenharmony_ci 32562306a36Sopenharmony_ci /* Make sure all traps are set up for that destination */ 32662306a36Sopenharmony_ci list_for_each_entry(trap, &block_vcap_is2->rules, list) { 32762306a36Sopenharmony_ci if (!trap->is_trap) 32862306a36Sopenharmony_ci continue; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* Figure out the current trapping destination */ 33162306a36Sopenharmony_ci if (using_tag_8021q) { 33262306a36Sopenharmony_ci /* Redirect to the tag_8021q CPU port. If timestamps 33362306a36Sopenharmony_ci * are necessary, also copy trapped packets to the CPU 33462306a36Sopenharmony_ci * port module. 33562306a36Sopenharmony_ci */ 33662306a36Sopenharmony_ci mask_mode = OCELOT_MASK_MODE_REDIRECT; 33762306a36Sopenharmony_ci port_mask = BIT(felix_trap_get_cpu_port(ds, trap)); 33862306a36Sopenharmony_ci cpu_copy_ena = !!trap->take_ts; 33962306a36Sopenharmony_ci } else { 34062306a36Sopenharmony_ci /* Trap packets only to the CPU port module, which is 34162306a36Sopenharmony_ci * redirected to the NPI port (the DSA CPU port) 34262306a36Sopenharmony_ci */ 34362306a36Sopenharmony_ci mask_mode = OCELOT_MASK_MODE_PERMIT_DENY; 34462306a36Sopenharmony_ci port_mask = 0; 34562306a36Sopenharmony_ci cpu_copy_ena = true; 34662306a36Sopenharmony_ci } 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci if (trap->action.mask_mode == mask_mode && 34962306a36Sopenharmony_ci trap->action.port_mask == port_mask && 35062306a36Sopenharmony_ci trap->action.cpu_copy_ena == cpu_copy_ena) 35162306a36Sopenharmony_ci continue; 35262306a36Sopenharmony_ci 35362306a36Sopenharmony_ci trap->action.mask_mode = mask_mode; 35462306a36Sopenharmony_ci trap->action.port_mask = port_mask; 35562306a36Sopenharmony_ci trap->action.cpu_copy_ena = cpu_copy_ena; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci err = ocelot_vcap_filter_replace(ocelot, trap); 35862306a36Sopenharmony_ci if (err) 35962306a36Sopenharmony_ci return err; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci return 0; 36362306a36Sopenharmony_ci} 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci/* The CPU port module is connected to the Node Processor Interface (NPI). This 36662306a36Sopenharmony_ci * is the mode through which frames can be injected from and extracted to an 36762306a36Sopenharmony_ci * external CPU, over Ethernet. In NXP SoCs, the "external CPU" is the ARM CPU 36862306a36Sopenharmony_ci * running Linux, and this forms a DSA setup together with the enetc or fman 36962306a36Sopenharmony_ci * DSA master. 37062306a36Sopenharmony_ci */ 37162306a36Sopenharmony_cistatic void felix_npi_port_init(struct ocelot *ocelot, int port) 37262306a36Sopenharmony_ci{ 37362306a36Sopenharmony_ci ocelot->npi = port; 37462306a36Sopenharmony_ci 37562306a36Sopenharmony_ci ocelot_write(ocelot, QSYS_EXT_CPU_CFG_EXT_CPUQ_MSK_M | 37662306a36Sopenharmony_ci QSYS_EXT_CPU_CFG_EXT_CPU_PORT(port), 37762306a36Sopenharmony_ci QSYS_EXT_CPU_CFG); 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci /* NPI port Injection/Extraction configuration */ 38062306a36Sopenharmony_ci ocelot_fields_write(ocelot, port, SYS_PORT_MODE_INCL_XTR_HDR, 38162306a36Sopenharmony_ci ocelot->npi_xtr_prefix); 38262306a36Sopenharmony_ci ocelot_fields_write(ocelot, port, SYS_PORT_MODE_INCL_INJ_HDR, 38362306a36Sopenharmony_ci ocelot->npi_inj_prefix); 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci /* Disable transmission of pause frames */ 38662306a36Sopenharmony_ci ocelot_fields_write(ocelot, port, SYS_PAUSE_CFG_PAUSE_ENA, 0); 38762306a36Sopenharmony_ci} 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_cistatic void felix_npi_port_deinit(struct ocelot *ocelot, int port) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci /* Restore hardware defaults */ 39262306a36Sopenharmony_ci int unused_port = ocelot->num_phys_ports + 2; 39362306a36Sopenharmony_ci 39462306a36Sopenharmony_ci ocelot->npi = -1; 39562306a36Sopenharmony_ci 39662306a36Sopenharmony_ci ocelot_write(ocelot, QSYS_EXT_CPU_CFG_EXT_CPU_PORT(unused_port), 39762306a36Sopenharmony_ci QSYS_EXT_CPU_CFG); 39862306a36Sopenharmony_ci 39962306a36Sopenharmony_ci ocelot_fields_write(ocelot, port, SYS_PORT_MODE_INCL_XTR_HDR, 40062306a36Sopenharmony_ci OCELOT_TAG_PREFIX_DISABLED); 40162306a36Sopenharmony_ci ocelot_fields_write(ocelot, port, SYS_PORT_MODE_INCL_INJ_HDR, 40262306a36Sopenharmony_ci OCELOT_TAG_PREFIX_DISABLED); 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci /* Enable transmission of pause frames */ 40562306a36Sopenharmony_ci ocelot_fields_write(ocelot, port, SYS_PAUSE_CFG_PAUSE_ENA, 1); 40662306a36Sopenharmony_ci} 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_cistatic int felix_tag_npi_setup(struct dsa_switch *ds) 40962306a36Sopenharmony_ci{ 41062306a36Sopenharmony_ci struct dsa_port *dp, *first_cpu_dp = NULL; 41162306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci dsa_switch_for_each_user_port(dp, ds) { 41462306a36Sopenharmony_ci if (first_cpu_dp && dp->cpu_dp != first_cpu_dp) { 41562306a36Sopenharmony_ci dev_err(ds->dev, "Multiple NPI ports not supported\n"); 41662306a36Sopenharmony_ci return -EINVAL; 41762306a36Sopenharmony_ci } 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci first_cpu_dp = dp->cpu_dp; 42062306a36Sopenharmony_ci } 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci if (!first_cpu_dp) 42362306a36Sopenharmony_ci return -EINVAL; 42462306a36Sopenharmony_ci 42562306a36Sopenharmony_ci felix_npi_port_init(ocelot, first_cpu_dp->index); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci return 0; 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic void felix_tag_npi_teardown(struct dsa_switch *ds) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_ci felix_npi_port_deinit(ocelot, ocelot->npi); 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic unsigned long felix_tag_npi_get_host_fwd_mask(struct dsa_switch *ds) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci return BIT(ocelot->num_phys_ports); 44262306a36Sopenharmony_ci} 44362306a36Sopenharmony_ci 44462306a36Sopenharmony_cistatic int felix_tag_npi_change_master(struct dsa_switch *ds, int port, 44562306a36Sopenharmony_ci struct net_device *master, 44662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 44762306a36Sopenharmony_ci{ 44862306a36Sopenharmony_ci struct dsa_port *dp = dsa_to_port(ds, port), *other_dp; 44962306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci if (netif_is_lag_master(master)) { 45262306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 45362306a36Sopenharmony_ci "LAG DSA master only supported using ocelot-8021q"); 45462306a36Sopenharmony_ci return -EOPNOTSUPP; 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci /* Changing the NPI port breaks user ports still assigned to the old 45862306a36Sopenharmony_ci * one, so only allow it while they're down, and don't allow them to 45962306a36Sopenharmony_ci * come back up until they're all changed to the new one. 46062306a36Sopenharmony_ci */ 46162306a36Sopenharmony_ci dsa_switch_for_each_user_port(other_dp, ds) { 46262306a36Sopenharmony_ci struct net_device *slave = other_dp->slave; 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci if (other_dp != dp && (slave->flags & IFF_UP) && 46562306a36Sopenharmony_ci dsa_port_to_master(other_dp) != master) { 46662306a36Sopenharmony_ci NL_SET_ERR_MSG_MOD(extack, 46762306a36Sopenharmony_ci "Cannot change while old master still has users"); 46862306a36Sopenharmony_ci return -EOPNOTSUPP; 46962306a36Sopenharmony_ci } 47062306a36Sopenharmony_ci } 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_ci felix_npi_port_deinit(ocelot, ocelot->npi); 47362306a36Sopenharmony_ci felix_npi_port_init(ocelot, felix_cpu_port_for_master(ds, master)); 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci return 0; 47662306a36Sopenharmony_ci} 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci/* Alternatively to using the NPI functionality, that same hardware MAC 47962306a36Sopenharmony_ci * connected internally to the enetc or fman DSA master can be configured to 48062306a36Sopenharmony_ci * use the software-defined tag_8021q frame format. As far as the hardware is 48162306a36Sopenharmony_ci * concerned, it thinks it is a "dumb switch" - the queues of the CPU port 48262306a36Sopenharmony_ci * module are now disconnected from it, but can still be accessed through 48362306a36Sopenharmony_ci * register-based MMIO. 48462306a36Sopenharmony_ci */ 48562306a36Sopenharmony_cistatic const struct felix_tag_proto_ops felix_tag_npi_proto_ops = { 48662306a36Sopenharmony_ci .setup = felix_tag_npi_setup, 48762306a36Sopenharmony_ci .teardown = felix_tag_npi_teardown, 48862306a36Sopenharmony_ci .get_host_fwd_mask = felix_tag_npi_get_host_fwd_mask, 48962306a36Sopenharmony_ci .change_master = felix_tag_npi_change_master, 49062306a36Sopenharmony_ci}; 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistatic int felix_tag_8021q_setup(struct dsa_switch *ds) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 49562306a36Sopenharmony_ci struct dsa_port *dp; 49662306a36Sopenharmony_ci int err; 49762306a36Sopenharmony_ci 49862306a36Sopenharmony_ci err = dsa_tag_8021q_register(ds, htons(ETH_P_8021AD)); 49962306a36Sopenharmony_ci if (err) 50062306a36Sopenharmony_ci return err; 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci dsa_switch_for_each_cpu_port(dp, ds) 50362306a36Sopenharmony_ci ocelot_port_setup_dsa_8021q_cpu(ocelot, dp->index); 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci dsa_switch_for_each_user_port(dp, ds) 50662306a36Sopenharmony_ci ocelot_port_assign_dsa_8021q_cpu(ocelot, dp->index, 50762306a36Sopenharmony_ci dp->cpu_dp->index); 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci dsa_switch_for_each_available_port(dp, ds) 51062306a36Sopenharmony_ci /* This overwrites ocelot_init(): 51162306a36Sopenharmony_ci * Do not forward BPDU frames to the CPU port module, 51262306a36Sopenharmony_ci * for 2 reasons: 51362306a36Sopenharmony_ci * - When these packets are injected from the tag_8021q 51462306a36Sopenharmony_ci * CPU port, we want them to go out, not loop back 51562306a36Sopenharmony_ci * into the system. 51662306a36Sopenharmony_ci * - STP traffic ingressing on a user port should go to 51762306a36Sopenharmony_ci * the tag_8021q CPU port, not to the hardware CPU 51862306a36Sopenharmony_ci * port module. 51962306a36Sopenharmony_ci */ 52062306a36Sopenharmony_ci ocelot_write_gix(ocelot, 52162306a36Sopenharmony_ci ANA_PORT_CPU_FWD_BPDU_CFG_BPDU_REDIR_ENA(0), 52262306a36Sopenharmony_ci ANA_PORT_CPU_FWD_BPDU_CFG, dp->index); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci /* The ownership of the CPU port module's queues might have just been 52562306a36Sopenharmony_ci * transferred to the tag_8021q tagger from the NPI-based tagger. 52662306a36Sopenharmony_ci * So there might still be all sorts of crap in the queues. On the 52762306a36Sopenharmony_ci * other hand, the MMIO-based matching of PTP frames is very brittle, 52862306a36Sopenharmony_ci * so we need to be careful that there are no extra frames to be 52962306a36Sopenharmony_ci * dequeued over MMIO, since we would never know to discard them. 53062306a36Sopenharmony_ci */ 53162306a36Sopenharmony_ci ocelot_drain_cpu_queue(ocelot, 0); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci return 0; 53462306a36Sopenharmony_ci} 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_cistatic void felix_tag_8021q_teardown(struct dsa_switch *ds) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 53962306a36Sopenharmony_ci struct dsa_port *dp; 54062306a36Sopenharmony_ci 54162306a36Sopenharmony_ci dsa_switch_for_each_available_port(dp, ds) 54262306a36Sopenharmony_ci /* Restore the logic from ocelot_init: 54362306a36Sopenharmony_ci * do not forward BPDU frames to the front ports. 54462306a36Sopenharmony_ci */ 54562306a36Sopenharmony_ci ocelot_write_gix(ocelot, 54662306a36Sopenharmony_ci ANA_PORT_CPU_FWD_BPDU_CFG_BPDU_REDIR_ENA(0xffff), 54762306a36Sopenharmony_ci ANA_PORT_CPU_FWD_BPDU_CFG, 54862306a36Sopenharmony_ci dp->index); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci dsa_switch_for_each_user_port(dp, ds) 55162306a36Sopenharmony_ci ocelot_port_unassign_dsa_8021q_cpu(ocelot, dp->index); 55262306a36Sopenharmony_ci 55362306a36Sopenharmony_ci dsa_switch_for_each_cpu_port(dp, ds) 55462306a36Sopenharmony_ci ocelot_port_teardown_dsa_8021q_cpu(ocelot, dp->index); 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci dsa_tag_8021q_unregister(ds); 55762306a36Sopenharmony_ci} 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_cistatic unsigned long felix_tag_8021q_get_host_fwd_mask(struct dsa_switch *ds) 56062306a36Sopenharmony_ci{ 56162306a36Sopenharmony_ci return dsa_cpu_ports(ds); 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cistatic int felix_tag_8021q_change_master(struct dsa_switch *ds, int port, 56562306a36Sopenharmony_ci struct net_device *master, 56662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 56762306a36Sopenharmony_ci{ 56862306a36Sopenharmony_ci int cpu = felix_cpu_port_for_master(ds, master); 56962306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_ci ocelot_port_unassign_dsa_8021q_cpu(ocelot, port); 57262306a36Sopenharmony_ci ocelot_port_assign_dsa_8021q_cpu(ocelot, port, cpu); 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci return felix_update_trapping_destinations(ds, true); 57562306a36Sopenharmony_ci} 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_cistatic const struct felix_tag_proto_ops felix_tag_8021q_proto_ops = { 57862306a36Sopenharmony_ci .setup = felix_tag_8021q_setup, 57962306a36Sopenharmony_ci .teardown = felix_tag_8021q_teardown, 58062306a36Sopenharmony_ci .get_host_fwd_mask = felix_tag_8021q_get_host_fwd_mask, 58162306a36Sopenharmony_ci .change_master = felix_tag_8021q_change_master, 58262306a36Sopenharmony_ci}; 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_cistatic void felix_set_host_flood(struct dsa_switch *ds, unsigned long mask, 58562306a36Sopenharmony_ci bool uc, bool mc, bool bc) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 58862306a36Sopenharmony_ci unsigned long val; 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci val = uc ? mask : 0; 59162306a36Sopenharmony_ci ocelot_rmw_rix(ocelot, val, mask, ANA_PGID_PGID, PGID_UC); 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci val = mc ? mask : 0; 59462306a36Sopenharmony_ci ocelot_rmw_rix(ocelot, val, mask, ANA_PGID_PGID, PGID_MC); 59562306a36Sopenharmony_ci ocelot_rmw_rix(ocelot, val, mask, ANA_PGID_PGID, PGID_MCIPV4); 59662306a36Sopenharmony_ci ocelot_rmw_rix(ocelot, val, mask, ANA_PGID_PGID, PGID_MCIPV6); 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci val = bc ? mask : 0; 59962306a36Sopenharmony_ci ocelot_rmw_rix(ocelot, val, mask, ANA_PGID_PGID, PGID_BC); 60062306a36Sopenharmony_ci} 60162306a36Sopenharmony_ci 60262306a36Sopenharmony_cistatic void 60362306a36Sopenharmony_cifelix_migrate_host_flood(struct dsa_switch *ds, 60462306a36Sopenharmony_ci const struct felix_tag_proto_ops *proto_ops, 60562306a36Sopenharmony_ci const struct felix_tag_proto_ops *old_proto_ops) 60662306a36Sopenharmony_ci{ 60762306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 60862306a36Sopenharmony_ci struct felix *felix = ocelot_to_felix(ocelot); 60962306a36Sopenharmony_ci unsigned long mask; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci if (old_proto_ops) { 61262306a36Sopenharmony_ci mask = old_proto_ops->get_host_fwd_mask(ds); 61362306a36Sopenharmony_ci felix_set_host_flood(ds, mask, false, false, false); 61462306a36Sopenharmony_ci } 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_ci mask = proto_ops->get_host_fwd_mask(ds); 61762306a36Sopenharmony_ci felix_set_host_flood(ds, mask, !!felix->host_flood_uc_mask, 61862306a36Sopenharmony_ci !!felix->host_flood_mc_mask, true); 61962306a36Sopenharmony_ci} 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_cistatic int felix_migrate_mdbs(struct dsa_switch *ds, 62262306a36Sopenharmony_ci const struct felix_tag_proto_ops *proto_ops, 62362306a36Sopenharmony_ci const struct felix_tag_proto_ops *old_proto_ops) 62462306a36Sopenharmony_ci{ 62562306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 62662306a36Sopenharmony_ci unsigned long from, to; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci if (!old_proto_ops) 62962306a36Sopenharmony_ci return 0; 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci from = old_proto_ops->get_host_fwd_mask(ds); 63262306a36Sopenharmony_ci to = proto_ops->get_host_fwd_mask(ds); 63362306a36Sopenharmony_ci 63462306a36Sopenharmony_ci return ocelot_migrate_mdbs(ocelot, from, to); 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_ci/* Configure the shared hardware resources for a transition between 63862306a36Sopenharmony_ci * @old_proto_ops and @proto_ops. 63962306a36Sopenharmony_ci * Manual migration is needed because as far as DSA is concerned, no change of 64062306a36Sopenharmony_ci * the CPU port is taking place here, just of the tagging protocol. 64162306a36Sopenharmony_ci */ 64262306a36Sopenharmony_cistatic int 64362306a36Sopenharmony_cifelix_tag_proto_setup_shared(struct dsa_switch *ds, 64462306a36Sopenharmony_ci const struct felix_tag_proto_ops *proto_ops, 64562306a36Sopenharmony_ci const struct felix_tag_proto_ops *old_proto_ops) 64662306a36Sopenharmony_ci{ 64762306a36Sopenharmony_ci bool using_tag_8021q = (proto_ops == &felix_tag_8021q_proto_ops); 64862306a36Sopenharmony_ci int err; 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci err = felix_migrate_mdbs(ds, proto_ops, old_proto_ops); 65162306a36Sopenharmony_ci if (err) 65262306a36Sopenharmony_ci return err; 65362306a36Sopenharmony_ci 65462306a36Sopenharmony_ci felix_update_trapping_destinations(ds, using_tag_8021q); 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci felix_migrate_host_flood(ds, proto_ops, old_proto_ops); 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci return 0; 65962306a36Sopenharmony_ci} 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci/* This always leaves the switch in a consistent state, because although the 66262306a36Sopenharmony_ci * tag_8021q setup can fail, the NPI setup can't. So either the change is made, 66362306a36Sopenharmony_ci * or the restoration is guaranteed to work. 66462306a36Sopenharmony_ci */ 66562306a36Sopenharmony_cistatic int felix_change_tag_protocol(struct dsa_switch *ds, 66662306a36Sopenharmony_ci enum dsa_tag_protocol proto) 66762306a36Sopenharmony_ci{ 66862306a36Sopenharmony_ci const struct felix_tag_proto_ops *old_proto_ops, *proto_ops; 66962306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 67062306a36Sopenharmony_ci struct felix *felix = ocelot_to_felix(ocelot); 67162306a36Sopenharmony_ci int err; 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci switch (proto) { 67462306a36Sopenharmony_ci case DSA_TAG_PROTO_SEVILLE: 67562306a36Sopenharmony_ci case DSA_TAG_PROTO_OCELOT: 67662306a36Sopenharmony_ci proto_ops = &felix_tag_npi_proto_ops; 67762306a36Sopenharmony_ci break; 67862306a36Sopenharmony_ci case DSA_TAG_PROTO_OCELOT_8021Q: 67962306a36Sopenharmony_ci proto_ops = &felix_tag_8021q_proto_ops; 68062306a36Sopenharmony_ci break; 68162306a36Sopenharmony_ci default: 68262306a36Sopenharmony_ci return -EPROTONOSUPPORT; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci old_proto_ops = felix->tag_proto_ops; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci if (proto_ops == old_proto_ops) 68862306a36Sopenharmony_ci return 0; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci err = proto_ops->setup(ds); 69162306a36Sopenharmony_ci if (err) 69262306a36Sopenharmony_ci goto setup_failed; 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_ci err = felix_tag_proto_setup_shared(ds, proto_ops, old_proto_ops); 69562306a36Sopenharmony_ci if (err) 69662306a36Sopenharmony_ci goto setup_shared_failed; 69762306a36Sopenharmony_ci 69862306a36Sopenharmony_ci if (old_proto_ops) 69962306a36Sopenharmony_ci old_proto_ops->teardown(ds); 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_ci felix->tag_proto_ops = proto_ops; 70262306a36Sopenharmony_ci felix->tag_proto = proto; 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_ci return 0; 70562306a36Sopenharmony_ci 70662306a36Sopenharmony_cisetup_shared_failed: 70762306a36Sopenharmony_ci proto_ops->teardown(ds); 70862306a36Sopenharmony_cisetup_failed: 70962306a36Sopenharmony_ci return err; 71062306a36Sopenharmony_ci} 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cistatic enum dsa_tag_protocol felix_get_tag_protocol(struct dsa_switch *ds, 71362306a36Sopenharmony_ci int port, 71462306a36Sopenharmony_ci enum dsa_tag_protocol mp) 71562306a36Sopenharmony_ci{ 71662306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 71762306a36Sopenharmony_ci struct felix *felix = ocelot_to_felix(ocelot); 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci return felix->tag_proto; 72062306a36Sopenharmony_ci} 72162306a36Sopenharmony_ci 72262306a36Sopenharmony_cistatic void felix_port_set_host_flood(struct dsa_switch *ds, int port, 72362306a36Sopenharmony_ci bool uc, bool mc) 72462306a36Sopenharmony_ci{ 72562306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 72662306a36Sopenharmony_ci struct felix *felix = ocelot_to_felix(ocelot); 72762306a36Sopenharmony_ci unsigned long mask; 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_ci if (uc) 73062306a36Sopenharmony_ci felix->host_flood_uc_mask |= BIT(port); 73162306a36Sopenharmony_ci else 73262306a36Sopenharmony_ci felix->host_flood_uc_mask &= ~BIT(port); 73362306a36Sopenharmony_ci 73462306a36Sopenharmony_ci if (mc) 73562306a36Sopenharmony_ci felix->host_flood_mc_mask |= BIT(port); 73662306a36Sopenharmony_ci else 73762306a36Sopenharmony_ci felix->host_flood_mc_mask &= ~BIT(port); 73862306a36Sopenharmony_ci 73962306a36Sopenharmony_ci mask = felix->tag_proto_ops->get_host_fwd_mask(ds); 74062306a36Sopenharmony_ci felix_set_host_flood(ds, mask, !!felix->host_flood_uc_mask, 74162306a36Sopenharmony_ci !!felix->host_flood_mc_mask, true); 74262306a36Sopenharmony_ci} 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_cistatic int felix_port_change_master(struct dsa_switch *ds, int port, 74562306a36Sopenharmony_ci struct net_device *master, 74662306a36Sopenharmony_ci struct netlink_ext_ack *extack) 74762306a36Sopenharmony_ci{ 74862306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 74962306a36Sopenharmony_ci struct felix *felix = ocelot_to_felix(ocelot); 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci return felix->tag_proto_ops->change_master(ds, port, master, extack); 75262306a36Sopenharmony_ci} 75362306a36Sopenharmony_ci 75462306a36Sopenharmony_cistatic int felix_set_ageing_time(struct dsa_switch *ds, 75562306a36Sopenharmony_ci unsigned int ageing_time) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 75862306a36Sopenharmony_ci 75962306a36Sopenharmony_ci ocelot_set_ageing_time(ocelot, ageing_time); 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci return 0; 76262306a36Sopenharmony_ci} 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_cistatic void felix_port_fast_age(struct dsa_switch *ds, int port) 76562306a36Sopenharmony_ci{ 76662306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 76762306a36Sopenharmony_ci int err; 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_ci err = ocelot_mact_flush(ocelot, port); 77062306a36Sopenharmony_ci if (err) 77162306a36Sopenharmony_ci dev_err(ds->dev, "Flushing MAC table on port %d returned %pe\n", 77262306a36Sopenharmony_ci port, ERR_PTR(err)); 77362306a36Sopenharmony_ci} 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_cistatic int felix_fdb_dump(struct dsa_switch *ds, int port, 77662306a36Sopenharmony_ci dsa_fdb_dump_cb_t *cb, void *data) 77762306a36Sopenharmony_ci{ 77862306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_ci return ocelot_fdb_dump(ocelot, port, cb, data); 78162306a36Sopenharmony_ci} 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_cistatic int felix_fdb_add(struct dsa_switch *ds, int port, 78462306a36Sopenharmony_ci const unsigned char *addr, u16 vid, 78562306a36Sopenharmony_ci struct dsa_db db) 78662306a36Sopenharmony_ci{ 78762306a36Sopenharmony_ci struct net_device *bridge_dev = felix_classify_db(db); 78862306a36Sopenharmony_ci struct dsa_port *dp = dsa_to_port(ds, port); 78962306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 79062306a36Sopenharmony_ci 79162306a36Sopenharmony_ci if (IS_ERR(bridge_dev)) 79262306a36Sopenharmony_ci return PTR_ERR(bridge_dev); 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci if (dsa_port_is_cpu(dp) && !bridge_dev && 79562306a36Sopenharmony_ci dsa_fdb_present_in_other_db(ds, port, addr, vid, db)) 79662306a36Sopenharmony_ci return 0; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci if (dsa_port_is_cpu(dp)) 79962306a36Sopenharmony_ci port = PGID_CPU; 80062306a36Sopenharmony_ci 80162306a36Sopenharmony_ci return ocelot_fdb_add(ocelot, port, addr, vid, bridge_dev); 80262306a36Sopenharmony_ci} 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_cistatic int felix_fdb_del(struct dsa_switch *ds, int port, 80562306a36Sopenharmony_ci const unsigned char *addr, u16 vid, 80662306a36Sopenharmony_ci struct dsa_db db) 80762306a36Sopenharmony_ci{ 80862306a36Sopenharmony_ci struct net_device *bridge_dev = felix_classify_db(db); 80962306a36Sopenharmony_ci struct dsa_port *dp = dsa_to_port(ds, port); 81062306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 81162306a36Sopenharmony_ci 81262306a36Sopenharmony_ci if (IS_ERR(bridge_dev)) 81362306a36Sopenharmony_ci return PTR_ERR(bridge_dev); 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_ci if (dsa_port_is_cpu(dp) && !bridge_dev && 81662306a36Sopenharmony_ci dsa_fdb_present_in_other_db(ds, port, addr, vid, db)) 81762306a36Sopenharmony_ci return 0; 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci if (dsa_port_is_cpu(dp)) 82062306a36Sopenharmony_ci port = PGID_CPU; 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci return ocelot_fdb_del(ocelot, port, addr, vid, bridge_dev); 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_cistatic int felix_lag_fdb_add(struct dsa_switch *ds, struct dsa_lag lag, 82662306a36Sopenharmony_ci const unsigned char *addr, u16 vid, 82762306a36Sopenharmony_ci struct dsa_db db) 82862306a36Sopenharmony_ci{ 82962306a36Sopenharmony_ci struct net_device *bridge_dev = felix_classify_db(db); 83062306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci if (IS_ERR(bridge_dev)) 83362306a36Sopenharmony_ci return PTR_ERR(bridge_dev); 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci return ocelot_lag_fdb_add(ocelot, lag.dev, addr, vid, bridge_dev); 83662306a36Sopenharmony_ci} 83762306a36Sopenharmony_ci 83862306a36Sopenharmony_cistatic int felix_lag_fdb_del(struct dsa_switch *ds, struct dsa_lag lag, 83962306a36Sopenharmony_ci const unsigned char *addr, u16 vid, 84062306a36Sopenharmony_ci struct dsa_db db) 84162306a36Sopenharmony_ci{ 84262306a36Sopenharmony_ci struct net_device *bridge_dev = felix_classify_db(db); 84362306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 84462306a36Sopenharmony_ci 84562306a36Sopenharmony_ci if (IS_ERR(bridge_dev)) 84662306a36Sopenharmony_ci return PTR_ERR(bridge_dev); 84762306a36Sopenharmony_ci 84862306a36Sopenharmony_ci return ocelot_lag_fdb_del(ocelot, lag.dev, addr, vid, bridge_dev); 84962306a36Sopenharmony_ci} 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_cistatic int felix_mdb_add(struct dsa_switch *ds, int port, 85262306a36Sopenharmony_ci const struct switchdev_obj_port_mdb *mdb, 85362306a36Sopenharmony_ci struct dsa_db db) 85462306a36Sopenharmony_ci{ 85562306a36Sopenharmony_ci struct net_device *bridge_dev = felix_classify_db(db); 85662306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 85762306a36Sopenharmony_ci 85862306a36Sopenharmony_ci if (IS_ERR(bridge_dev)) 85962306a36Sopenharmony_ci return PTR_ERR(bridge_dev); 86062306a36Sopenharmony_ci 86162306a36Sopenharmony_ci if (dsa_is_cpu_port(ds, port) && !bridge_dev && 86262306a36Sopenharmony_ci dsa_mdb_present_in_other_db(ds, port, mdb, db)) 86362306a36Sopenharmony_ci return 0; 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_ci if (port == ocelot->npi) 86662306a36Sopenharmony_ci port = ocelot->num_phys_ports; 86762306a36Sopenharmony_ci 86862306a36Sopenharmony_ci return ocelot_port_mdb_add(ocelot, port, mdb, bridge_dev); 86962306a36Sopenharmony_ci} 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_cistatic int felix_mdb_del(struct dsa_switch *ds, int port, 87262306a36Sopenharmony_ci const struct switchdev_obj_port_mdb *mdb, 87362306a36Sopenharmony_ci struct dsa_db db) 87462306a36Sopenharmony_ci{ 87562306a36Sopenharmony_ci struct net_device *bridge_dev = felix_classify_db(db); 87662306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci if (IS_ERR(bridge_dev)) 87962306a36Sopenharmony_ci return PTR_ERR(bridge_dev); 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_ci if (dsa_is_cpu_port(ds, port) && !bridge_dev && 88262306a36Sopenharmony_ci dsa_mdb_present_in_other_db(ds, port, mdb, db)) 88362306a36Sopenharmony_ci return 0; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci if (port == ocelot->npi) 88662306a36Sopenharmony_ci port = ocelot->num_phys_ports; 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci return ocelot_port_mdb_del(ocelot, port, mdb, bridge_dev); 88962306a36Sopenharmony_ci} 89062306a36Sopenharmony_ci 89162306a36Sopenharmony_cistatic void felix_bridge_stp_state_set(struct dsa_switch *ds, int port, 89262306a36Sopenharmony_ci u8 state) 89362306a36Sopenharmony_ci{ 89462306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci return ocelot_bridge_stp_state_set(ocelot, port, state); 89762306a36Sopenharmony_ci} 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_cistatic int felix_pre_bridge_flags(struct dsa_switch *ds, int port, 90062306a36Sopenharmony_ci struct switchdev_brport_flags val, 90162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 90262306a36Sopenharmony_ci{ 90362306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci return ocelot_port_pre_bridge_flags(ocelot, port, val); 90662306a36Sopenharmony_ci} 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_cistatic int felix_bridge_flags(struct dsa_switch *ds, int port, 90962306a36Sopenharmony_ci struct switchdev_brport_flags val, 91062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 91162306a36Sopenharmony_ci{ 91262306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 91362306a36Sopenharmony_ci 91462306a36Sopenharmony_ci if (port == ocelot->npi) 91562306a36Sopenharmony_ci port = ocelot->num_phys_ports; 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci ocelot_port_bridge_flags(ocelot, port, val); 91862306a36Sopenharmony_ci 91962306a36Sopenharmony_ci return 0; 92062306a36Sopenharmony_ci} 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_cistatic int felix_bridge_join(struct dsa_switch *ds, int port, 92362306a36Sopenharmony_ci struct dsa_bridge bridge, bool *tx_fwd_offload, 92462306a36Sopenharmony_ci struct netlink_ext_ack *extack) 92562306a36Sopenharmony_ci{ 92662306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_ci return ocelot_port_bridge_join(ocelot, port, bridge.dev, bridge.num, 92962306a36Sopenharmony_ci extack); 93062306a36Sopenharmony_ci} 93162306a36Sopenharmony_ci 93262306a36Sopenharmony_cistatic void felix_bridge_leave(struct dsa_switch *ds, int port, 93362306a36Sopenharmony_ci struct dsa_bridge bridge) 93462306a36Sopenharmony_ci{ 93562306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci ocelot_port_bridge_leave(ocelot, port, bridge.dev); 93862306a36Sopenharmony_ci} 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_cistatic int felix_lag_join(struct dsa_switch *ds, int port, 94162306a36Sopenharmony_ci struct dsa_lag lag, 94262306a36Sopenharmony_ci struct netdev_lag_upper_info *info, 94362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 94462306a36Sopenharmony_ci{ 94562306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 94662306a36Sopenharmony_ci int err; 94762306a36Sopenharmony_ci 94862306a36Sopenharmony_ci err = ocelot_port_lag_join(ocelot, port, lag.dev, info, extack); 94962306a36Sopenharmony_ci if (err) 95062306a36Sopenharmony_ci return err; 95162306a36Sopenharmony_ci 95262306a36Sopenharmony_ci /* Update the logical LAG port that serves as tag_8021q CPU port */ 95362306a36Sopenharmony_ci if (!dsa_is_cpu_port(ds, port)) 95462306a36Sopenharmony_ci return 0; 95562306a36Sopenharmony_ci 95662306a36Sopenharmony_ci return felix_port_change_master(ds, port, lag.dev, extack); 95762306a36Sopenharmony_ci} 95862306a36Sopenharmony_ci 95962306a36Sopenharmony_cistatic int felix_lag_leave(struct dsa_switch *ds, int port, 96062306a36Sopenharmony_ci struct dsa_lag lag) 96162306a36Sopenharmony_ci{ 96262306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci ocelot_port_lag_leave(ocelot, port, lag.dev); 96562306a36Sopenharmony_ci 96662306a36Sopenharmony_ci /* Update the logical LAG port that serves as tag_8021q CPU port */ 96762306a36Sopenharmony_ci if (!dsa_is_cpu_port(ds, port)) 96862306a36Sopenharmony_ci return 0; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci return felix_port_change_master(ds, port, lag.dev, NULL); 97162306a36Sopenharmony_ci} 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_cistatic int felix_lag_change(struct dsa_switch *ds, int port) 97462306a36Sopenharmony_ci{ 97562306a36Sopenharmony_ci struct dsa_port *dp = dsa_to_port(ds, port); 97662306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci ocelot_port_lag_change(ocelot, port, dp->lag_tx_enabled); 97962306a36Sopenharmony_ci 98062306a36Sopenharmony_ci return 0; 98162306a36Sopenharmony_ci} 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_cistatic int felix_vlan_prepare(struct dsa_switch *ds, int port, 98462306a36Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan, 98562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 98662306a36Sopenharmony_ci{ 98762306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 98862306a36Sopenharmony_ci u16 flags = vlan->flags; 98962306a36Sopenharmony_ci 99062306a36Sopenharmony_ci /* Ocelot switches copy frames as-is to the CPU, so the flags: 99162306a36Sopenharmony_ci * egress-untagged or not, pvid or not, make no difference. This 99262306a36Sopenharmony_ci * behavior is already better than what DSA just tries to approximate 99362306a36Sopenharmony_ci * when it installs the VLAN with the same flags on the CPU port. 99462306a36Sopenharmony_ci * Just accept any configuration, and don't let ocelot deny installing 99562306a36Sopenharmony_ci * multiple native VLANs on the NPI port, because the switch doesn't 99662306a36Sopenharmony_ci * look at the port tag settings towards the NPI interface anyway. 99762306a36Sopenharmony_ci */ 99862306a36Sopenharmony_ci if (port == ocelot->npi) 99962306a36Sopenharmony_ci return 0; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci return ocelot_vlan_prepare(ocelot, port, vlan->vid, 100262306a36Sopenharmony_ci flags & BRIDGE_VLAN_INFO_PVID, 100362306a36Sopenharmony_ci flags & BRIDGE_VLAN_INFO_UNTAGGED, 100462306a36Sopenharmony_ci extack); 100562306a36Sopenharmony_ci} 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_cistatic int felix_vlan_filtering(struct dsa_switch *ds, int port, bool enabled, 100862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 100962306a36Sopenharmony_ci{ 101062306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 101162306a36Sopenharmony_ci 101262306a36Sopenharmony_ci return ocelot_port_vlan_filtering(ocelot, port, enabled, extack); 101362306a36Sopenharmony_ci} 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_cistatic int felix_vlan_add(struct dsa_switch *ds, int port, 101662306a36Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan, 101762306a36Sopenharmony_ci struct netlink_ext_ack *extack) 101862306a36Sopenharmony_ci{ 101962306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 102062306a36Sopenharmony_ci u16 flags = vlan->flags; 102162306a36Sopenharmony_ci int err; 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci err = felix_vlan_prepare(ds, port, vlan, extack); 102462306a36Sopenharmony_ci if (err) 102562306a36Sopenharmony_ci return err; 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci return ocelot_vlan_add(ocelot, port, vlan->vid, 102862306a36Sopenharmony_ci flags & BRIDGE_VLAN_INFO_PVID, 102962306a36Sopenharmony_ci flags & BRIDGE_VLAN_INFO_UNTAGGED); 103062306a36Sopenharmony_ci} 103162306a36Sopenharmony_ci 103262306a36Sopenharmony_cistatic int felix_vlan_del(struct dsa_switch *ds, int port, 103362306a36Sopenharmony_ci const struct switchdev_obj_port_vlan *vlan) 103462306a36Sopenharmony_ci{ 103562306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 103662306a36Sopenharmony_ci 103762306a36Sopenharmony_ci return ocelot_vlan_del(ocelot, port, vlan->vid); 103862306a36Sopenharmony_ci} 103962306a36Sopenharmony_ci 104062306a36Sopenharmony_cistatic void felix_phylink_get_caps(struct dsa_switch *ds, int port, 104162306a36Sopenharmony_ci struct phylink_config *config) 104262306a36Sopenharmony_ci{ 104362306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci config->mac_capabilities = MAC_ASYM_PAUSE | MAC_SYM_PAUSE | 104662306a36Sopenharmony_ci MAC_10 | MAC_100 | MAC_1000FD | 104762306a36Sopenharmony_ci MAC_2500FD; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci __set_bit(ocelot->ports[port]->phy_mode, 105062306a36Sopenharmony_ci config->supported_interfaces); 105162306a36Sopenharmony_ci} 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_cistatic void felix_phylink_mac_config(struct dsa_switch *ds, int port, 105462306a36Sopenharmony_ci unsigned int mode, 105562306a36Sopenharmony_ci const struct phylink_link_state *state) 105662306a36Sopenharmony_ci{ 105762306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 105862306a36Sopenharmony_ci struct felix *felix = ocelot_to_felix(ocelot); 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci if (felix->info->phylink_mac_config) 106162306a36Sopenharmony_ci felix->info->phylink_mac_config(ocelot, port, mode, state); 106262306a36Sopenharmony_ci} 106362306a36Sopenharmony_ci 106462306a36Sopenharmony_cistatic struct phylink_pcs *felix_phylink_mac_select_pcs(struct dsa_switch *ds, 106562306a36Sopenharmony_ci int port, 106662306a36Sopenharmony_ci phy_interface_t iface) 106762306a36Sopenharmony_ci{ 106862306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 106962306a36Sopenharmony_ci struct felix *felix = ocelot_to_felix(ocelot); 107062306a36Sopenharmony_ci struct phylink_pcs *pcs = NULL; 107162306a36Sopenharmony_ci 107262306a36Sopenharmony_ci if (felix->pcs && felix->pcs[port]) 107362306a36Sopenharmony_ci pcs = felix->pcs[port]; 107462306a36Sopenharmony_ci 107562306a36Sopenharmony_ci return pcs; 107662306a36Sopenharmony_ci} 107762306a36Sopenharmony_ci 107862306a36Sopenharmony_cistatic void felix_phylink_mac_link_down(struct dsa_switch *ds, int port, 107962306a36Sopenharmony_ci unsigned int link_an_mode, 108062306a36Sopenharmony_ci phy_interface_t interface) 108162306a36Sopenharmony_ci{ 108262306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 108362306a36Sopenharmony_ci struct felix *felix; 108462306a36Sopenharmony_ci 108562306a36Sopenharmony_ci felix = ocelot_to_felix(ocelot); 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci ocelot_phylink_mac_link_down(ocelot, port, link_an_mode, interface, 108862306a36Sopenharmony_ci felix->info->quirks); 108962306a36Sopenharmony_ci} 109062306a36Sopenharmony_ci 109162306a36Sopenharmony_cistatic void felix_phylink_mac_link_up(struct dsa_switch *ds, int port, 109262306a36Sopenharmony_ci unsigned int link_an_mode, 109362306a36Sopenharmony_ci phy_interface_t interface, 109462306a36Sopenharmony_ci struct phy_device *phydev, 109562306a36Sopenharmony_ci int speed, int duplex, 109662306a36Sopenharmony_ci bool tx_pause, bool rx_pause) 109762306a36Sopenharmony_ci{ 109862306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 109962306a36Sopenharmony_ci struct felix *felix = ocelot_to_felix(ocelot); 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci ocelot_phylink_mac_link_up(ocelot, port, phydev, link_an_mode, 110262306a36Sopenharmony_ci interface, speed, duplex, tx_pause, rx_pause, 110362306a36Sopenharmony_ci felix->info->quirks); 110462306a36Sopenharmony_ci 110562306a36Sopenharmony_ci if (felix->info->port_sched_speed_set) 110662306a36Sopenharmony_ci felix->info->port_sched_speed_set(ocelot, port, speed); 110762306a36Sopenharmony_ci} 110862306a36Sopenharmony_ci 110962306a36Sopenharmony_cistatic int felix_port_enable(struct dsa_switch *ds, int port, 111062306a36Sopenharmony_ci struct phy_device *phydev) 111162306a36Sopenharmony_ci{ 111262306a36Sopenharmony_ci struct dsa_port *dp = dsa_to_port(ds, port); 111362306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci if (!dsa_port_is_user(dp)) 111662306a36Sopenharmony_ci return 0; 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci if (ocelot->npi >= 0) { 111962306a36Sopenharmony_ci struct net_device *master = dsa_port_to_master(dp); 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci if (felix_cpu_port_for_master(ds, master) != ocelot->npi) { 112262306a36Sopenharmony_ci dev_err(ds->dev, "Multiple masters are not allowed\n"); 112362306a36Sopenharmony_ci return -EINVAL; 112462306a36Sopenharmony_ci } 112562306a36Sopenharmony_ci } 112662306a36Sopenharmony_ci 112762306a36Sopenharmony_ci return 0; 112862306a36Sopenharmony_ci} 112962306a36Sopenharmony_ci 113062306a36Sopenharmony_cistatic void felix_port_qos_map_init(struct ocelot *ocelot, int port) 113162306a36Sopenharmony_ci{ 113262306a36Sopenharmony_ci int i; 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci ocelot_rmw_gix(ocelot, 113562306a36Sopenharmony_ci ANA_PORT_QOS_CFG_QOS_PCP_ENA, 113662306a36Sopenharmony_ci ANA_PORT_QOS_CFG_QOS_PCP_ENA, 113762306a36Sopenharmony_ci ANA_PORT_QOS_CFG, 113862306a36Sopenharmony_ci port); 113962306a36Sopenharmony_ci 114062306a36Sopenharmony_ci for (i = 0; i < OCELOT_NUM_TC * 2; i++) { 114162306a36Sopenharmony_ci ocelot_rmw_ix(ocelot, 114262306a36Sopenharmony_ci (ANA_PORT_PCP_DEI_MAP_DP_PCP_DEI_VAL & i) | 114362306a36Sopenharmony_ci ANA_PORT_PCP_DEI_MAP_QOS_PCP_DEI_VAL(i), 114462306a36Sopenharmony_ci ANA_PORT_PCP_DEI_MAP_DP_PCP_DEI_VAL | 114562306a36Sopenharmony_ci ANA_PORT_PCP_DEI_MAP_QOS_PCP_DEI_VAL_M, 114662306a36Sopenharmony_ci ANA_PORT_PCP_DEI_MAP, 114762306a36Sopenharmony_ci port, i); 114862306a36Sopenharmony_ci } 114962306a36Sopenharmony_ci} 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_cistatic void felix_get_stats64(struct dsa_switch *ds, int port, 115262306a36Sopenharmony_ci struct rtnl_link_stats64 *stats) 115362306a36Sopenharmony_ci{ 115462306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 115562306a36Sopenharmony_ci 115662306a36Sopenharmony_ci ocelot_port_get_stats64(ocelot, port, stats); 115762306a36Sopenharmony_ci} 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_cistatic void felix_get_pause_stats(struct dsa_switch *ds, int port, 116062306a36Sopenharmony_ci struct ethtool_pause_stats *pause_stats) 116162306a36Sopenharmony_ci{ 116262306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 116362306a36Sopenharmony_ci 116462306a36Sopenharmony_ci ocelot_port_get_pause_stats(ocelot, port, pause_stats); 116562306a36Sopenharmony_ci} 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_cistatic void felix_get_rmon_stats(struct dsa_switch *ds, int port, 116862306a36Sopenharmony_ci struct ethtool_rmon_stats *rmon_stats, 116962306a36Sopenharmony_ci const struct ethtool_rmon_hist_range **ranges) 117062306a36Sopenharmony_ci{ 117162306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci ocelot_port_get_rmon_stats(ocelot, port, rmon_stats, ranges); 117462306a36Sopenharmony_ci} 117562306a36Sopenharmony_ci 117662306a36Sopenharmony_cistatic void felix_get_eth_ctrl_stats(struct dsa_switch *ds, int port, 117762306a36Sopenharmony_ci struct ethtool_eth_ctrl_stats *ctrl_stats) 117862306a36Sopenharmony_ci{ 117962306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci ocelot_port_get_eth_ctrl_stats(ocelot, port, ctrl_stats); 118262306a36Sopenharmony_ci} 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_cistatic void felix_get_eth_mac_stats(struct dsa_switch *ds, int port, 118562306a36Sopenharmony_ci struct ethtool_eth_mac_stats *mac_stats) 118662306a36Sopenharmony_ci{ 118762306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 118862306a36Sopenharmony_ci 118962306a36Sopenharmony_ci ocelot_port_get_eth_mac_stats(ocelot, port, mac_stats); 119062306a36Sopenharmony_ci} 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_cistatic void felix_get_eth_phy_stats(struct dsa_switch *ds, int port, 119362306a36Sopenharmony_ci struct ethtool_eth_phy_stats *phy_stats) 119462306a36Sopenharmony_ci{ 119562306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 119662306a36Sopenharmony_ci 119762306a36Sopenharmony_ci ocelot_port_get_eth_phy_stats(ocelot, port, phy_stats); 119862306a36Sopenharmony_ci} 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_cistatic void felix_get_strings(struct dsa_switch *ds, int port, 120162306a36Sopenharmony_ci u32 stringset, u8 *data) 120262306a36Sopenharmony_ci{ 120362306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci return ocelot_get_strings(ocelot, port, stringset, data); 120662306a36Sopenharmony_ci} 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_cistatic void felix_get_ethtool_stats(struct dsa_switch *ds, int port, u64 *data) 120962306a36Sopenharmony_ci{ 121062306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 121162306a36Sopenharmony_ci 121262306a36Sopenharmony_ci ocelot_get_ethtool_stats(ocelot, port, data); 121362306a36Sopenharmony_ci} 121462306a36Sopenharmony_ci 121562306a36Sopenharmony_cistatic int felix_get_sset_count(struct dsa_switch *ds, int port, int sset) 121662306a36Sopenharmony_ci{ 121762306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 121862306a36Sopenharmony_ci 121962306a36Sopenharmony_ci return ocelot_get_sset_count(ocelot, port, sset); 122062306a36Sopenharmony_ci} 122162306a36Sopenharmony_ci 122262306a36Sopenharmony_cistatic int felix_get_ts_info(struct dsa_switch *ds, int port, 122362306a36Sopenharmony_ci struct ethtool_ts_info *info) 122462306a36Sopenharmony_ci{ 122562306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci return ocelot_get_ts_info(ocelot, port, info); 122862306a36Sopenharmony_ci} 122962306a36Sopenharmony_ci 123062306a36Sopenharmony_cistatic const u32 felix_phy_match_table[PHY_INTERFACE_MODE_MAX] = { 123162306a36Sopenharmony_ci [PHY_INTERFACE_MODE_INTERNAL] = OCELOT_PORT_MODE_INTERNAL, 123262306a36Sopenharmony_ci [PHY_INTERFACE_MODE_SGMII] = OCELOT_PORT_MODE_SGMII, 123362306a36Sopenharmony_ci [PHY_INTERFACE_MODE_QSGMII] = OCELOT_PORT_MODE_QSGMII, 123462306a36Sopenharmony_ci [PHY_INTERFACE_MODE_USXGMII] = OCELOT_PORT_MODE_USXGMII, 123562306a36Sopenharmony_ci [PHY_INTERFACE_MODE_1000BASEX] = OCELOT_PORT_MODE_1000BASEX, 123662306a36Sopenharmony_ci [PHY_INTERFACE_MODE_2500BASEX] = OCELOT_PORT_MODE_2500BASEX, 123762306a36Sopenharmony_ci}; 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_cistatic int felix_validate_phy_mode(struct felix *felix, int port, 124062306a36Sopenharmony_ci phy_interface_t phy_mode) 124162306a36Sopenharmony_ci{ 124262306a36Sopenharmony_ci u32 modes = felix->info->port_modes[port]; 124362306a36Sopenharmony_ci 124462306a36Sopenharmony_ci if (felix_phy_match_table[phy_mode] & modes) 124562306a36Sopenharmony_ci return 0; 124662306a36Sopenharmony_ci return -EOPNOTSUPP; 124762306a36Sopenharmony_ci} 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_cistatic int felix_parse_ports_node(struct felix *felix, 125062306a36Sopenharmony_ci struct device_node *ports_node, 125162306a36Sopenharmony_ci phy_interface_t *port_phy_modes) 125262306a36Sopenharmony_ci{ 125362306a36Sopenharmony_ci struct device *dev = felix->ocelot.dev; 125462306a36Sopenharmony_ci struct device_node *child; 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci for_each_available_child_of_node(ports_node, child) { 125762306a36Sopenharmony_ci phy_interface_t phy_mode; 125862306a36Sopenharmony_ci u32 port; 125962306a36Sopenharmony_ci int err; 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci /* Get switch port number from DT */ 126262306a36Sopenharmony_ci if (of_property_read_u32(child, "reg", &port) < 0) { 126362306a36Sopenharmony_ci dev_err(dev, "Port number not defined in device tree " 126462306a36Sopenharmony_ci "(property \"reg\")\n"); 126562306a36Sopenharmony_ci of_node_put(child); 126662306a36Sopenharmony_ci return -ENODEV; 126762306a36Sopenharmony_ci } 126862306a36Sopenharmony_ci 126962306a36Sopenharmony_ci /* Get PHY mode from DT */ 127062306a36Sopenharmony_ci err = of_get_phy_mode(child, &phy_mode); 127162306a36Sopenharmony_ci if (err) { 127262306a36Sopenharmony_ci dev_err(dev, "Failed to read phy-mode or " 127362306a36Sopenharmony_ci "phy-interface-type property for port %d\n", 127462306a36Sopenharmony_ci port); 127562306a36Sopenharmony_ci of_node_put(child); 127662306a36Sopenharmony_ci return -ENODEV; 127762306a36Sopenharmony_ci } 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci err = felix_validate_phy_mode(felix, port, phy_mode); 128062306a36Sopenharmony_ci if (err < 0) { 128162306a36Sopenharmony_ci dev_info(dev, "Unsupported PHY mode %s on port %d\n", 128262306a36Sopenharmony_ci phy_modes(phy_mode), port); 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci /* Leave port_phy_modes[port] = 0, which is also 128562306a36Sopenharmony_ci * PHY_INTERFACE_MODE_NA. This will perform a 128662306a36Sopenharmony_ci * best-effort to bring up as many ports as possible. 128762306a36Sopenharmony_ci */ 128862306a36Sopenharmony_ci continue; 128962306a36Sopenharmony_ci } 129062306a36Sopenharmony_ci 129162306a36Sopenharmony_ci port_phy_modes[port] = phy_mode; 129262306a36Sopenharmony_ci } 129362306a36Sopenharmony_ci 129462306a36Sopenharmony_ci return 0; 129562306a36Sopenharmony_ci} 129662306a36Sopenharmony_ci 129762306a36Sopenharmony_cistatic int felix_parse_dt(struct felix *felix, phy_interface_t *port_phy_modes) 129862306a36Sopenharmony_ci{ 129962306a36Sopenharmony_ci struct device *dev = felix->ocelot.dev; 130062306a36Sopenharmony_ci struct device_node *switch_node; 130162306a36Sopenharmony_ci struct device_node *ports_node; 130262306a36Sopenharmony_ci int err; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci switch_node = dev->of_node; 130562306a36Sopenharmony_ci 130662306a36Sopenharmony_ci ports_node = of_get_child_by_name(switch_node, "ports"); 130762306a36Sopenharmony_ci if (!ports_node) 130862306a36Sopenharmony_ci ports_node = of_get_child_by_name(switch_node, "ethernet-ports"); 130962306a36Sopenharmony_ci if (!ports_node) { 131062306a36Sopenharmony_ci dev_err(dev, "Incorrect bindings: absent \"ports\" or \"ethernet-ports\" node\n"); 131162306a36Sopenharmony_ci return -ENODEV; 131262306a36Sopenharmony_ci } 131362306a36Sopenharmony_ci 131462306a36Sopenharmony_ci err = felix_parse_ports_node(felix, ports_node, port_phy_modes); 131562306a36Sopenharmony_ci of_node_put(ports_node); 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci return err; 131862306a36Sopenharmony_ci} 131962306a36Sopenharmony_ci 132062306a36Sopenharmony_cistatic struct regmap *felix_request_regmap_by_name(struct felix *felix, 132162306a36Sopenharmony_ci const char *resource_name) 132262306a36Sopenharmony_ci{ 132362306a36Sopenharmony_ci struct ocelot *ocelot = &felix->ocelot; 132462306a36Sopenharmony_ci struct resource res; 132562306a36Sopenharmony_ci int i; 132662306a36Sopenharmony_ci 132762306a36Sopenharmony_ci /* In an MFD configuration, regmaps are registered directly to the 132862306a36Sopenharmony_ci * parent device before the child devices are probed, so there is no 132962306a36Sopenharmony_ci * need to initialize a new one. 133062306a36Sopenharmony_ci */ 133162306a36Sopenharmony_ci if (!felix->info->resources) 133262306a36Sopenharmony_ci return dev_get_regmap(ocelot->dev->parent, resource_name); 133362306a36Sopenharmony_ci 133462306a36Sopenharmony_ci for (i = 0; i < felix->info->num_resources; i++) { 133562306a36Sopenharmony_ci if (strcmp(resource_name, felix->info->resources[i].name)) 133662306a36Sopenharmony_ci continue; 133762306a36Sopenharmony_ci 133862306a36Sopenharmony_ci memcpy(&res, &felix->info->resources[i], sizeof(res)); 133962306a36Sopenharmony_ci res.start += felix->switch_base; 134062306a36Sopenharmony_ci res.end += felix->switch_base; 134162306a36Sopenharmony_ci 134262306a36Sopenharmony_ci return ocelot_regmap_init(ocelot, &res); 134362306a36Sopenharmony_ci } 134462306a36Sopenharmony_ci 134562306a36Sopenharmony_ci return ERR_PTR(-ENOENT); 134662306a36Sopenharmony_ci} 134762306a36Sopenharmony_ci 134862306a36Sopenharmony_cistatic struct regmap *felix_request_regmap(struct felix *felix, 134962306a36Sopenharmony_ci enum ocelot_target target) 135062306a36Sopenharmony_ci{ 135162306a36Sopenharmony_ci const char *resource_name = felix->info->resource_names[target]; 135262306a36Sopenharmony_ci 135362306a36Sopenharmony_ci /* If the driver didn't provide a resource name for the target, 135462306a36Sopenharmony_ci * the resource is optional. 135562306a36Sopenharmony_ci */ 135662306a36Sopenharmony_ci if (!resource_name) 135762306a36Sopenharmony_ci return NULL; 135862306a36Sopenharmony_ci 135962306a36Sopenharmony_ci return felix_request_regmap_by_name(felix, resource_name); 136062306a36Sopenharmony_ci} 136162306a36Sopenharmony_ci 136262306a36Sopenharmony_cistatic struct regmap *felix_request_port_regmap(struct felix *felix, int port) 136362306a36Sopenharmony_ci{ 136462306a36Sopenharmony_ci char resource_name[32]; 136562306a36Sopenharmony_ci 136662306a36Sopenharmony_ci sprintf(resource_name, "port%d", port); 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_ci return felix_request_regmap_by_name(felix, resource_name); 136962306a36Sopenharmony_ci} 137062306a36Sopenharmony_ci 137162306a36Sopenharmony_cistatic int felix_init_structs(struct felix *felix, int num_phys_ports) 137262306a36Sopenharmony_ci{ 137362306a36Sopenharmony_ci struct ocelot *ocelot = &felix->ocelot; 137462306a36Sopenharmony_ci phy_interface_t *port_phy_modes; 137562306a36Sopenharmony_ci struct regmap *target; 137662306a36Sopenharmony_ci int port, i, err; 137762306a36Sopenharmony_ci 137862306a36Sopenharmony_ci ocelot->num_phys_ports = num_phys_ports; 137962306a36Sopenharmony_ci ocelot->ports = devm_kcalloc(ocelot->dev, num_phys_ports, 138062306a36Sopenharmony_ci sizeof(struct ocelot_port *), GFP_KERNEL); 138162306a36Sopenharmony_ci if (!ocelot->ports) 138262306a36Sopenharmony_ci return -ENOMEM; 138362306a36Sopenharmony_ci 138462306a36Sopenharmony_ci ocelot->map = felix->info->map; 138562306a36Sopenharmony_ci ocelot->num_mact_rows = felix->info->num_mact_rows; 138662306a36Sopenharmony_ci ocelot->vcap = felix->info->vcap; 138762306a36Sopenharmony_ci ocelot->vcap_pol.base = felix->info->vcap_pol_base; 138862306a36Sopenharmony_ci ocelot->vcap_pol.max = felix->info->vcap_pol_max; 138962306a36Sopenharmony_ci ocelot->vcap_pol.base2 = felix->info->vcap_pol_base2; 139062306a36Sopenharmony_ci ocelot->vcap_pol.max2 = felix->info->vcap_pol_max2; 139162306a36Sopenharmony_ci ocelot->ops = felix->info->ops; 139262306a36Sopenharmony_ci ocelot->npi_inj_prefix = OCELOT_TAG_PREFIX_SHORT; 139362306a36Sopenharmony_ci ocelot->npi_xtr_prefix = OCELOT_TAG_PREFIX_SHORT; 139462306a36Sopenharmony_ci ocelot->devlink = felix->ds->devlink; 139562306a36Sopenharmony_ci 139662306a36Sopenharmony_ci port_phy_modes = kcalloc(num_phys_ports, sizeof(phy_interface_t), 139762306a36Sopenharmony_ci GFP_KERNEL); 139862306a36Sopenharmony_ci if (!port_phy_modes) 139962306a36Sopenharmony_ci return -ENOMEM; 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci err = felix_parse_dt(felix, port_phy_modes); 140262306a36Sopenharmony_ci if (err) { 140362306a36Sopenharmony_ci kfree(port_phy_modes); 140462306a36Sopenharmony_ci return err; 140562306a36Sopenharmony_ci } 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci for (i = 0; i < TARGET_MAX; i++) { 140862306a36Sopenharmony_ci target = felix_request_regmap(felix, i); 140962306a36Sopenharmony_ci if (IS_ERR(target)) { 141062306a36Sopenharmony_ci dev_err(ocelot->dev, 141162306a36Sopenharmony_ci "Failed to map device memory space: %pe\n", 141262306a36Sopenharmony_ci target); 141362306a36Sopenharmony_ci kfree(port_phy_modes); 141462306a36Sopenharmony_ci return PTR_ERR(target); 141562306a36Sopenharmony_ci } 141662306a36Sopenharmony_ci 141762306a36Sopenharmony_ci ocelot->targets[i] = target; 141862306a36Sopenharmony_ci } 141962306a36Sopenharmony_ci 142062306a36Sopenharmony_ci err = ocelot_regfields_init(ocelot, felix->info->regfields); 142162306a36Sopenharmony_ci if (err) { 142262306a36Sopenharmony_ci dev_err(ocelot->dev, "failed to init reg fields map\n"); 142362306a36Sopenharmony_ci kfree(port_phy_modes); 142462306a36Sopenharmony_ci return err; 142562306a36Sopenharmony_ci } 142662306a36Sopenharmony_ci 142762306a36Sopenharmony_ci for (port = 0; port < num_phys_ports; port++) { 142862306a36Sopenharmony_ci struct ocelot_port *ocelot_port; 142962306a36Sopenharmony_ci 143062306a36Sopenharmony_ci ocelot_port = devm_kzalloc(ocelot->dev, 143162306a36Sopenharmony_ci sizeof(struct ocelot_port), 143262306a36Sopenharmony_ci GFP_KERNEL); 143362306a36Sopenharmony_ci if (!ocelot_port) { 143462306a36Sopenharmony_ci dev_err(ocelot->dev, 143562306a36Sopenharmony_ci "failed to allocate port memory\n"); 143662306a36Sopenharmony_ci kfree(port_phy_modes); 143762306a36Sopenharmony_ci return -ENOMEM; 143862306a36Sopenharmony_ci } 143962306a36Sopenharmony_ci 144062306a36Sopenharmony_ci target = felix_request_port_regmap(felix, port); 144162306a36Sopenharmony_ci if (IS_ERR(target)) { 144262306a36Sopenharmony_ci dev_err(ocelot->dev, 144362306a36Sopenharmony_ci "Failed to map memory space for port %d: %pe\n", 144462306a36Sopenharmony_ci port, target); 144562306a36Sopenharmony_ci kfree(port_phy_modes); 144662306a36Sopenharmony_ci return PTR_ERR(target); 144762306a36Sopenharmony_ci } 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_ci ocelot_port->phy_mode = port_phy_modes[port]; 145062306a36Sopenharmony_ci ocelot_port->ocelot = ocelot; 145162306a36Sopenharmony_ci ocelot_port->target = target; 145262306a36Sopenharmony_ci ocelot_port->index = port; 145362306a36Sopenharmony_ci ocelot->ports[port] = ocelot_port; 145462306a36Sopenharmony_ci } 145562306a36Sopenharmony_ci 145662306a36Sopenharmony_ci kfree(port_phy_modes); 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci if (felix->info->mdio_bus_alloc) { 145962306a36Sopenharmony_ci err = felix->info->mdio_bus_alloc(ocelot); 146062306a36Sopenharmony_ci if (err < 0) 146162306a36Sopenharmony_ci return err; 146262306a36Sopenharmony_ci } 146362306a36Sopenharmony_ci 146462306a36Sopenharmony_ci return 0; 146562306a36Sopenharmony_ci} 146662306a36Sopenharmony_ci 146762306a36Sopenharmony_cistatic void ocelot_port_purge_txtstamp_skb(struct ocelot *ocelot, int port, 146862306a36Sopenharmony_ci struct sk_buff *skb) 146962306a36Sopenharmony_ci{ 147062306a36Sopenharmony_ci struct ocelot_port *ocelot_port = ocelot->ports[port]; 147162306a36Sopenharmony_ci struct sk_buff *clone = OCELOT_SKB_CB(skb)->clone; 147262306a36Sopenharmony_ci struct sk_buff *skb_match = NULL, *skb_tmp; 147362306a36Sopenharmony_ci unsigned long flags; 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci if (!clone) 147662306a36Sopenharmony_ci return; 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci spin_lock_irqsave(&ocelot_port->tx_skbs.lock, flags); 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci skb_queue_walk_safe(&ocelot_port->tx_skbs, skb, skb_tmp) { 148162306a36Sopenharmony_ci if (skb != clone) 148262306a36Sopenharmony_ci continue; 148362306a36Sopenharmony_ci __skb_unlink(skb, &ocelot_port->tx_skbs); 148462306a36Sopenharmony_ci skb_match = skb; 148562306a36Sopenharmony_ci break; 148662306a36Sopenharmony_ci } 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci spin_unlock_irqrestore(&ocelot_port->tx_skbs.lock, flags); 148962306a36Sopenharmony_ci 149062306a36Sopenharmony_ci WARN_ONCE(!skb_match, 149162306a36Sopenharmony_ci "Could not find skb clone in TX timestamping list\n"); 149262306a36Sopenharmony_ci} 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci#define work_to_xmit_work(w) \ 149562306a36Sopenharmony_ci container_of((w), struct felix_deferred_xmit_work, work) 149662306a36Sopenharmony_ci 149762306a36Sopenharmony_cistatic void felix_port_deferred_xmit(struct kthread_work *work) 149862306a36Sopenharmony_ci{ 149962306a36Sopenharmony_ci struct felix_deferred_xmit_work *xmit_work = work_to_xmit_work(work); 150062306a36Sopenharmony_ci struct dsa_switch *ds = xmit_work->dp->ds; 150162306a36Sopenharmony_ci struct sk_buff *skb = xmit_work->skb; 150262306a36Sopenharmony_ci u32 rew_op = ocelot_ptp_rew_op(skb); 150362306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 150462306a36Sopenharmony_ci int port = xmit_work->dp->index; 150562306a36Sopenharmony_ci int retries = 10; 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci do { 150862306a36Sopenharmony_ci if (ocelot_can_inject(ocelot, 0)) 150962306a36Sopenharmony_ci break; 151062306a36Sopenharmony_ci 151162306a36Sopenharmony_ci cpu_relax(); 151262306a36Sopenharmony_ci } while (--retries); 151362306a36Sopenharmony_ci 151462306a36Sopenharmony_ci if (!retries) { 151562306a36Sopenharmony_ci dev_err(ocelot->dev, "port %d failed to inject skb\n", 151662306a36Sopenharmony_ci port); 151762306a36Sopenharmony_ci ocelot_port_purge_txtstamp_skb(ocelot, port, skb); 151862306a36Sopenharmony_ci kfree_skb(skb); 151962306a36Sopenharmony_ci return; 152062306a36Sopenharmony_ci } 152162306a36Sopenharmony_ci 152262306a36Sopenharmony_ci ocelot_port_inject_frame(ocelot, port, 0, rew_op, skb); 152362306a36Sopenharmony_ci 152462306a36Sopenharmony_ci consume_skb(skb); 152562306a36Sopenharmony_ci kfree(xmit_work); 152662306a36Sopenharmony_ci} 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_cistatic int felix_connect_tag_protocol(struct dsa_switch *ds, 152962306a36Sopenharmony_ci enum dsa_tag_protocol proto) 153062306a36Sopenharmony_ci{ 153162306a36Sopenharmony_ci struct ocelot_8021q_tagger_data *tagger_data; 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci switch (proto) { 153462306a36Sopenharmony_ci case DSA_TAG_PROTO_OCELOT_8021Q: 153562306a36Sopenharmony_ci tagger_data = ocelot_8021q_tagger_data(ds); 153662306a36Sopenharmony_ci tagger_data->xmit_work_fn = felix_port_deferred_xmit; 153762306a36Sopenharmony_ci return 0; 153862306a36Sopenharmony_ci case DSA_TAG_PROTO_OCELOT: 153962306a36Sopenharmony_ci case DSA_TAG_PROTO_SEVILLE: 154062306a36Sopenharmony_ci return 0; 154162306a36Sopenharmony_ci default: 154262306a36Sopenharmony_ci return -EPROTONOSUPPORT; 154362306a36Sopenharmony_ci } 154462306a36Sopenharmony_ci} 154562306a36Sopenharmony_ci 154662306a36Sopenharmony_cistatic int felix_setup(struct dsa_switch *ds) 154762306a36Sopenharmony_ci{ 154862306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 154962306a36Sopenharmony_ci struct felix *felix = ocelot_to_felix(ocelot); 155062306a36Sopenharmony_ci struct dsa_port *dp; 155162306a36Sopenharmony_ci int err; 155262306a36Sopenharmony_ci 155362306a36Sopenharmony_ci err = felix_init_structs(felix, ds->num_ports); 155462306a36Sopenharmony_ci if (err) 155562306a36Sopenharmony_ci return err; 155662306a36Sopenharmony_ci 155762306a36Sopenharmony_ci if (ocelot->targets[HSIO]) 155862306a36Sopenharmony_ci ocelot_pll5_init(ocelot); 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci err = ocelot_init(ocelot); 156162306a36Sopenharmony_ci if (err) 156262306a36Sopenharmony_ci goto out_mdiobus_free; 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_ci if (ocelot->ptp) { 156562306a36Sopenharmony_ci err = ocelot_init_timestamp(ocelot, felix->info->ptp_caps); 156662306a36Sopenharmony_ci if (err) { 156762306a36Sopenharmony_ci dev_err(ocelot->dev, 156862306a36Sopenharmony_ci "Timestamp initialization failed\n"); 156962306a36Sopenharmony_ci ocelot->ptp = 0; 157062306a36Sopenharmony_ci } 157162306a36Sopenharmony_ci } 157262306a36Sopenharmony_ci 157362306a36Sopenharmony_ci dsa_switch_for_each_available_port(dp, ds) { 157462306a36Sopenharmony_ci ocelot_init_port(ocelot, dp->index); 157562306a36Sopenharmony_ci 157662306a36Sopenharmony_ci if (felix->info->configure_serdes) 157762306a36Sopenharmony_ci felix->info->configure_serdes(ocelot, dp->index, 157862306a36Sopenharmony_ci dp->dn); 157962306a36Sopenharmony_ci 158062306a36Sopenharmony_ci /* Set the default QoS Classification based on PCP and DEI 158162306a36Sopenharmony_ci * bits of vlan tag. 158262306a36Sopenharmony_ci */ 158362306a36Sopenharmony_ci felix_port_qos_map_init(ocelot, dp->index); 158462306a36Sopenharmony_ci } 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci err = ocelot_devlink_sb_register(ocelot); 158762306a36Sopenharmony_ci if (err) 158862306a36Sopenharmony_ci goto out_deinit_ports; 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci /* The initial tag protocol is NPI which won't fail during initial 159162306a36Sopenharmony_ci * setup, there's no real point in checking for errors. 159262306a36Sopenharmony_ci */ 159362306a36Sopenharmony_ci felix_change_tag_protocol(ds, felix->tag_proto); 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_ci ds->mtu_enforcement_ingress = true; 159662306a36Sopenharmony_ci ds->assisted_learning_on_cpu_port = true; 159762306a36Sopenharmony_ci ds->fdb_isolation = true; 159862306a36Sopenharmony_ci ds->max_num_bridges = ds->num_ports; 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci return 0; 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ciout_deinit_ports: 160362306a36Sopenharmony_ci dsa_switch_for_each_available_port(dp, ds) 160462306a36Sopenharmony_ci ocelot_deinit_port(ocelot, dp->index); 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci ocelot_deinit_timestamp(ocelot); 160762306a36Sopenharmony_ci ocelot_deinit(ocelot); 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ciout_mdiobus_free: 161062306a36Sopenharmony_ci if (felix->info->mdio_bus_free) 161162306a36Sopenharmony_ci felix->info->mdio_bus_free(ocelot); 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_ci return err; 161462306a36Sopenharmony_ci} 161562306a36Sopenharmony_ci 161662306a36Sopenharmony_cistatic void felix_teardown(struct dsa_switch *ds) 161762306a36Sopenharmony_ci{ 161862306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 161962306a36Sopenharmony_ci struct felix *felix = ocelot_to_felix(ocelot); 162062306a36Sopenharmony_ci struct dsa_port *dp; 162162306a36Sopenharmony_ci 162262306a36Sopenharmony_ci rtnl_lock(); 162362306a36Sopenharmony_ci if (felix->tag_proto_ops) 162462306a36Sopenharmony_ci felix->tag_proto_ops->teardown(ds); 162562306a36Sopenharmony_ci rtnl_unlock(); 162662306a36Sopenharmony_ci 162762306a36Sopenharmony_ci dsa_switch_for_each_available_port(dp, ds) 162862306a36Sopenharmony_ci ocelot_deinit_port(ocelot, dp->index); 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci ocelot_devlink_sb_unregister(ocelot); 163162306a36Sopenharmony_ci ocelot_deinit_timestamp(ocelot); 163262306a36Sopenharmony_ci ocelot_deinit(ocelot); 163362306a36Sopenharmony_ci 163462306a36Sopenharmony_ci if (felix->info->mdio_bus_free) 163562306a36Sopenharmony_ci felix->info->mdio_bus_free(ocelot); 163662306a36Sopenharmony_ci} 163762306a36Sopenharmony_ci 163862306a36Sopenharmony_cistatic int felix_hwtstamp_get(struct dsa_switch *ds, int port, 163962306a36Sopenharmony_ci struct ifreq *ifr) 164062306a36Sopenharmony_ci{ 164162306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci return ocelot_hwstamp_get(ocelot, port, ifr); 164462306a36Sopenharmony_ci} 164562306a36Sopenharmony_ci 164662306a36Sopenharmony_cistatic int felix_hwtstamp_set(struct dsa_switch *ds, int port, 164762306a36Sopenharmony_ci struct ifreq *ifr) 164862306a36Sopenharmony_ci{ 164962306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 165062306a36Sopenharmony_ci struct felix *felix = ocelot_to_felix(ocelot); 165162306a36Sopenharmony_ci bool using_tag_8021q; 165262306a36Sopenharmony_ci int err; 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci err = ocelot_hwstamp_set(ocelot, port, ifr); 165562306a36Sopenharmony_ci if (err) 165662306a36Sopenharmony_ci return err; 165762306a36Sopenharmony_ci 165862306a36Sopenharmony_ci using_tag_8021q = felix->tag_proto == DSA_TAG_PROTO_OCELOT_8021Q; 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci return felix_update_trapping_destinations(ds, using_tag_8021q); 166162306a36Sopenharmony_ci} 166262306a36Sopenharmony_ci 166362306a36Sopenharmony_cistatic bool felix_check_xtr_pkt(struct ocelot *ocelot) 166462306a36Sopenharmony_ci{ 166562306a36Sopenharmony_ci struct felix *felix = ocelot_to_felix(ocelot); 166662306a36Sopenharmony_ci int err = 0, grp = 0; 166762306a36Sopenharmony_ci 166862306a36Sopenharmony_ci if (felix->tag_proto != DSA_TAG_PROTO_OCELOT_8021Q) 166962306a36Sopenharmony_ci return false; 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci if (!felix->info->quirk_no_xtr_irq) 167262306a36Sopenharmony_ci return false; 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci while (ocelot_read(ocelot, QS_XTR_DATA_PRESENT) & BIT(grp)) { 167562306a36Sopenharmony_ci struct sk_buff *skb; 167662306a36Sopenharmony_ci unsigned int type; 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci err = ocelot_xtr_poll_frame(ocelot, grp, &skb); 167962306a36Sopenharmony_ci if (err) 168062306a36Sopenharmony_ci goto out; 168162306a36Sopenharmony_ci 168262306a36Sopenharmony_ci /* We trap to the CPU port module all PTP frames, but 168362306a36Sopenharmony_ci * felix_rxtstamp() only gets called for event frames. 168462306a36Sopenharmony_ci * So we need to avoid sending duplicate general 168562306a36Sopenharmony_ci * message frames by running a second BPF classifier 168662306a36Sopenharmony_ci * here and dropping those. 168762306a36Sopenharmony_ci */ 168862306a36Sopenharmony_ci __skb_push(skb, ETH_HLEN); 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci type = ptp_classify_raw(skb); 169162306a36Sopenharmony_ci 169262306a36Sopenharmony_ci __skb_pull(skb, ETH_HLEN); 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_ci if (type == PTP_CLASS_NONE) { 169562306a36Sopenharmony_ci kfree_skb(skb); 169662306a36Sopenharmony_ci continue; 169762306a36Sopenharmony_ci } 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci netif_rx(skb); 170062306a36Sopenharmony_ci } 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ciout: 170362306a36Sopenharmony_ci if (err < 0) { 170462306a36Sopenharmony_ci dev_err_ratelimited(ocelot->dev, 170562306a36Sopenharmony_ci "Error during packet extraction: %pe\n", 170662306a36Sopenharmony_ci ERR_PTR(err)); 170762306a36Sopenharmony_ci ocelot_drain_cpu_queue(ocelot, 0); 170862306a36Sopenharmony_ci } 170962306a36Sopenharmony_ci 171062306a36Sopenharmony_ci return true; 171162306a36Sopenharmony_ci} 171262306a36Sopenharmony_ci 171362306a36Sopenharmony_cistatic bool felix_rxtstamp(struct dsa_switch *ds, int port, 171462306a36Sopenharmony_ci struct sk_buff *skb, unsigned int type) 171562306a36Sopenharmony_ci{ 171662306a36Sopenharmony_ci u32 tstamp_lo = OCELOT_SKB_CB(skb)->tstamp_lo; 171762306a36Sopenharmony_ci struct skb_shared_hwtstamps *shhwtstamps; 171862306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 171962306a36Sopenharmony_ci struct timespec64 ts; 172062306a36Sopenharmony_ci u32 tstamp_hi; 172162306a36Sopenharmony_ci u64 tstamp; 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_ci switch (type & PTP_CLASS_PMASK) { 172462306a36Sopenharmony_ci case PTP_CLASS_L2: 172562306a36Sopenharmony_ci if (!(ocelot->ports[port]->trap_proto & OCELOT_PROTO_PTP_L2)) 172662306a36Sopenharmony_ci return false; 172762306a36Sopenharmony_ci break; 172862306a36Sopenharmony_ci case PTP_CLASS_IPV4: 172962306a36Sopenharmony_ci case PTP_CLASS_IPV6: 173062306a36Sopenharmony_ci if (!(ocelot->ports[port]->trap_proto & OCELOT_PROTO_PTP_L4)) 173162306a36Sopenharmony_ci return false; 173262306a36Sopenharmony_ci break; 173362306a36Sopenharmony_ci } 173462306a36Sopenharmony_ci 173562306a36Sopenharmony_ci /* If the "no XTR IRQ" workaround is in use, tell DSA to defer this skb 173662306a36Sopenharmony_ci * for RX timestamping. Then free it, and poll for its copy through 173762306a36Sopenharmony_ci * MMIO in the CPU port module, and inject that into the stack from 173862306a36Sopenharmony_ci * ocelot_xtr_poll(). 173962306a36Sopenharmony_ci */ 174062306a36Sopenharmony_ci if (felix_check_xtr_pkt(ocelot)) { 174162306a36Sopenharmony_ci kfree_skb(skb); 174262306a36Sopenharmony_ci return true; 174362306a36Sopenharmony_ci } 174462306a36Sopenharmony_ci 174562306a36Sopenharmony_ci ocelot_ptp_gettime64(&ocelot->ptp_info, &ts); 174662306a36Sopenharmony_ci tstamp = ktime_set(ts.tv_sec, ts.tv_nsec); 174762306a36Sopenharmony_ci 174862306a36Sopenharmony_ci tstamp_hi = tstamp >> 32; 174962306a36Sopenharmony_ci if ((tstamp & 0xffffffff) < tstamp_lo) 175062306a36Sopenharmony_ci tstamp_hi--; 175162306a36Sopenharmony_ci 175262306a36Sopenharmony_ci tstamp = ((u64)tstamp_hi << 32) | tstamp_lo; 175362306a36Sopenharmony_ci 175462306a36Sopenharmony_ci shhwtstamps = skb_hwtstamps(skb); 175562306a36Sopenharmony_ci memset(shhwtstamps, 0, sizeof(struct skb_shared_hwtstamps)); 175662306a36Sopenharmony_ci shhwtstamps->hwtstamp = tstamp; 175762306a36Sopenharmony_ci return false; 175862306a36Sopenharmony_ci} 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_cistatic void felix_txtstamp(struct dsa_switch *ds, int port, 176162306a36Sopenharmony_ci struct sk_buff *skb) 176262306a36Sopenharmony_ci{ 176362306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 176462306a36Sopenharmony_ci struct sk_buff *clone = NULL; 176562306a36Sopenharmony_ci 176662306a36Sopenharmony_ci if (!ocelot->ptp) 176762306a36Sopenharmony_ci return; 176862306a36Sopenharmony_ci 176962306a36Sopenharmony_ci if (ocelot_port_txtstamp_request(ocelot, port, skb, &clone)) { 177062306a36Sopenharmony_ci dev_err_ratelimited(ds->dev, 177162306a36Sopenharmony_ci "port %d delivering skb without TX timestamp\n", 177262306a36Sopenharmony_ci port); 177362306a36Sopenharmony_ci return; 177462306a36Sopenharmony_ci } 177562306a36Sopenharmony_ci 177662306a36Sopenharmony_ci if (clone) 177762306a36Sopenharmony_ci OCELOT_SKB_CB(skb)->clone = clone; 177862306a36Sopenharmony_ci} 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_cistatic int felix_change_mtu(struct dsa_switch *ds, int port, int new_mtu) 178162306a36Sopenharmony_ci{ 178262306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 178362306a36Sopenharmony_ci struct ocelot_port *ocelot_port = ocelot->ports[port]; 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci ocelot_port_set_maxlen(ocelot, port, new_mtu); 178662306a36Sopenharmony_ci 178762306a36Sopenharmony_ci mutex_lock(&ocelot->fwd_domain_lock); 178862306a36Sopenharmony_ci 178962306a36Sopenharmony_ci if (ocelot_port->taprio && ocelot->ops->tas_guard_bands_update) 179062306a36Sopenharmony_ci ocelot->ops->tas_guard_bands_update(ocelot, port); 179162306a36Sopenharmony_ci 179262306a36Sopenharmony_ci mutex_unlock(&ocelot->fwd_domain_lock); 179362306a36Sopenharmony_ci 179462306a36Sopenharmony_ci return 0; 179562306a36Sopenharmony_ci} 179662306a36Sopenharmony_ci 179762306a36Sopenharmony_cistatic int felix_get_max_mtu(struct dsa_switch *ds, int port) 179862306a36Sopenharmony_ci{ 179962306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci return ocelot_get_max_mtu(ocelot, port); 180262306a36Sopenharmony_ci} 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_cistatic int felix_cls_flower_add(struct dsa_switch *ds, int port, 180562306a36Sopenharmony_ci struct flow_cls_offload *cls, bool ingress) 180662306a36Sopenharmony_ci{ 180762306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 180862306a36Sopenharmony_ci struct felix *felix = ocelot_to_felix(ocelot); 180962306a36Sopenharmony_ci bool using_tag_8021q; 181062306a36Sopenharmony_ci int err; 181162306a36Sopenharmony_ci 181262306a36Sopenharmony_ci err = ocelot_cls_flower_replace(ocelot, port, cls, ingress); 181362306a36Sopenharmony_ci if (err) 181462306a36Sopenharmony_ci return err; 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci using_tag_8021q = felix->tag_proto == DSA_TAG_PROTO_OCELOT_8021Q; 181762306a36Sopenharmony_ci 181862306a36Sopenharmony_ci return felix_update_trapping_destinations(ds, using_tag_8021q); 181962306a36Sopenharmony_ci} 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_cistatic int felix_cls_flower_del(struct dsa_switch *ds, int port, 182262306a36Sopenharmony_ci struct flow_cls_offload *cls, bool ingress) 182362306a36Sopenharmony_ci{ 182462306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 182562306a36Sopenharmony_ci 182662306a36Sopenharmony_ci return ocelot_cls_flower_destroy(ocelot, port, cls, ingress); 182762306a36Sopenharmony_ci} 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_cistatic int felix_cls_flower_stats(struct dsa_switch *ds, int port, 183062306a36Sopenharmony_ci struct flow_cls_offload *cls, bool ingress) 183162306a36Sopenharmony_ci{ 183262306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci return ocelot_cls_flower_stats(ocelot, port, cls, ingress); 183562306a36Sopenharmony_ci} 183662306a36Sopenharmony_ci 183762306a36Sopenharmony_cistatic int felix_port_policer_add(struct dsa_switch *ds, int port, 183862306a36Sopenharmony_ci struct dsa_mall_policer_tc_entry *policer) 183962306a36Sopenharmony_ci{ 184062306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 184162306a36Sopenharmony_ci struct ocelot_policer pol = { 184262306a36Sopenharmony_ci .rate = div_u64(policer->rate_bytes_per_sec, 1000) * 8, 184362306a36Sopenharmony_ci .burst = policer->burst, 184462306a36Sopenharmony_ci }; 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_ci return ocelot_port_policer_add(ocelot, port, &pol); 184762306a36Sopenharmony_ci} 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_cistatic void felix_port_policer_del(struct dsa_switch *ds, int port) 185062306a36Sopenharmony_ci{ 185162306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 185262306a36Sopenharmony_ci 185362306a36Sopenharmony_ci ocelot_port_policer_del(ocelot, port); 185462306a36Sopenharmony_ci} 185562306a36Sopenharmony_ci 185662306a36Sopenharmony_cistatic int felix_port_mirror_add(struct dsa_switch *ds, int port, 185762306a36Sopenharmony_ci struct dsa_mall_mirror_tc_entry *mirror, 185862306a36Sopenharmony_ci bool ingress, struct netlink_ext_ack *extack) 185962306a36Sopenharmony_ci{ 186062306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci return ocelot_port_mirror_add(ocelot, port, mirror->to_local_port, 186362306a36Sopenharmony_ci ingress, extack); 186462306a36Sopenharmony_ci} 186562306a36Sopenharmony_ci 186662306a36Sopenharmony_cistatic void felix_port_mirror_del(struct dsa_switch *ds, int port, 186762306a36Sopenharmony_ci struct dsa_mall_mirror_tc_entry *mirror) 186862306a36Sopenharmony_ci{ 186962306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 187062306a36Sopenharmony_ci 187162306a36Sopenharmony_ci ocelot_port_mirror_del(ocelot, port, mirror->ingress); 187262306a36Sopenharmony_ci} 187362306a36Sopenharmony_ci 187462306a36Sopenharmony_cistatic int felix_port_setup_tc(struct dsa_switch *ds, int port, 187562306a36Sopenharmony_ci enum tc_setup_type type, 187662306a36Sopenharmony_ci void *type_data) 187762306a36Sopenharmony_ci{ 187862306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 187962306a36Sopenharmony_ci struct felix *felix = ocelot_to_felix(ocelot); 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci if (felix->info->port_setup_tc) 188262306a36Sopenharmony_ci return felix->info->port_setup_tc(ds, port, type, type_data); 188362306a36Sopenharmony_ci else 188462306a36Sopenharmony_ci return -EOPNOTSUPP; 188562306a36Sopenharmony_ci} 188662306a36Sopenharmony_ci 188762306a36Sopenharmony_cistatic int felix_sb_pool_get(struct dsa_switch *ds, unsigned int sb_index, 188862306a36Sopenharmony_ci u16 pool_index, 188962306a36Sopenharmony_ci struct devlink_sb_pool_info *pool_info) 189062306a36Sopenharmony_ci{ 189162306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci return ocelot_sb_pool_get(ocelot, sb_index, pool_index, pool_info); 189462306a36Sopenharmony_ci} 189562306a36Sopenharmony_ci 189662306a36Sopenharmony_cistatic int felix_sb_pool_set(struct dsa_switch *ds, unsigned int sb_index, 189762306a36Sopenharmony_ci u16 pool_index, u32 size, 189862306a36Sopenharmony_ci enum devlink_sb_threshold_type threshold_type, 189962306a36Sopenharmony_ci struct netlink_ext_ack *extack) 190062306a36Sopenharmony_ci{ 190162306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 190262306a36Sopenharmony_ci 190362306a36Sopenharmony_ci return ocelot_sb_pool_set(ocelot, sb_index, pool_index, size, 190462306a36Sopenharmony_ci threshold_type, extack); 190562306a36Sopenharmony_ci} 190662306a36Sopenharmony_ci 190762306a36Sopenharmony_cistatic int felix_sb_port_pool_get(struct dsa_switch *ds, int port, 190862306a36Sopenharmony_ci unsigned int sb_index, u16 pool_index, 190962306a36Sopenharmony_ci u32 *p_threshold) 191062306a36Sopenharmony_ci{ 191162306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 191262306a36Sopenharmony_ci 191362306a36Sopenharmony_ci return ocelot_sb_port_pool_get(ocelot, port, sb_index, pool_index, 191462306a36Sopenharmony_ci p_threshold); 191562306a36Sopenharmony_ci} 191662306a36Sopenharmony_ci 191762306a36Sopenharmony_cistatic int felix_sb_port_pool_set(struct dsa_switch *ds, int port, 191862306a36Sopenharmony_ci unsigned int sb_index, u16 pool_index, 191962306a36Sopenharmony_ci u32 threshold, struct netlink_ext_ack *extack) 192062306a36Sopenharmony_ci{ 192162306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 192262306a36Sopenharmony_ci 192362306a36Sopenharmony_ci return ocelot_sb_port_pool_set(ocelot, port, sb_index, pool_index, 192462306a36Sopenharmony_ci threshold, extack); 192562306a36Sopenharmony_ci} 192662306a36Sopenharmony_ci 192762306a36Sopenharmony_cistatic int felix_sb_tc_pool_bind_get(struct dsa_switch *ds, int port, 192862306a36Sopenharmony_ci unsigned int sb_index, u16 tc_index, 192962306a36Sopenharmony_ci enum devlink_sb_pool_type pool_type, 193062306a36Sopenharmony_ci u16 *p_pool_index, u32 *p_threshold) 193162306a36Sopenharmony_ci{ 193262306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 193362306a36Sopenharmony_ci 193462306a36Sopenharmony_ci return ocelot_sb_tc_pool_bind_get(ocelot, port, sb_index, tc_index, 193562306a36Sopenharmony_ci pool_type, p_pool_index, 193662306a36Sopenharmony_ci p_threshold); 193762306a36Sopenharmony_ci} 193862306a36Sopenharmony_ci 193962306a36Sopenharmony_cistatic int felix_sb_tc_pool_bind_set(struct dsa_switch *ds, int port, 194062306a36Sopenharmony_ci unsigned int sb_index, u16 tc_index, 194162306a36Sopenharmony_ci enum devlink_sb_pool_type pool_type, 194262306a36Sopenharmony_ci u16 pool_index, u32 threshold, 194362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 194462306a36Sopenharmony_ci{ 194562306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 194662306a36Sopenharmony_ci 194762306a36Sopenharmony_ci return ocelot_sb_tc_pool_bind_set(ocelot, port, sb_index, tc_index, 194862306a36Sopenharmony_ci pool_type, pool_index, threshold, 194962306a36Sopenharmony_ci extack); 195062306a36Sopenharmony_ci} 195162306a36Sopenharmony_ci 195262306a36Sopenharmony_cistatic int felix_sb_occ_snapshot(struct dsa_switch *ds, 195362306a36Sopenharmony_ci unsigned int sb_index) 195462306a36Sopenharmony_ci{ 195562306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 195662306a36Sopenharmony_ci 195762306a36Sopenharmony_ci return ocelot_sb_occ_snapshot(ocelot, sb_index); 195862306a36Sopenharmony_ci} 195962306a36Sopenharmony_ci 196062306a36Sopenharmony_cistatic int felix_sb_occ_max_clear(struct dsa_switch *ds, 196162306a36Sopenharmony_ci unsigned int sb_index) 196262306a36Sopenharmony_ci{ 196362306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 196462306a36Sopenharmony_ci 196562306a36Sopenharmony_ci return ocelot_sb_occ_max_clear(ocelot, sb_index); 196662306a36Sopenharmony_ci} 196762306a36Sopenharmony_ci 196862306a36Sopenharmony_cistatic int felix_sb_occ_port_pool_get(struct dsa_switch *ds, int port, 196962306a36Sopenharmony_ci unsigned int sb_index, u16 pool_index, 197062306a36Sopenharmony_ci u32 *p_cur, u32 *p_max) 197162306a36Sopenharmony_ci{ 197262306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci return ocelot_sb_occ_port_pool_get(ocelot, port, sb_index, pool_index, 197562306a36Sopenharmony_ci p_cur, p_max); 197662306a36Sopenharmony_ci} 197762306a36Sopenharmony_ci 197862306a36Sopenharmony_cistatic int felix_sb_occ_tc_port_bind_get(struct dsa_switch *ds, int port, 197962306a36Sopenharmony_ci unsigned int sb_index, u16 tc_index, 198062306a36Sopenharmony_ci enum devlink_sb_pool_type pool_type, 198162306a36Sopenharmony_ci u32 *p_cur, u32 *p_max) 198262306a36Sopenharmony_ci{ 198362306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_ci return ocelot_sb_occ_tc_port_bind_get(ocelot, port, sb_index, tc_index, 198662306a36Sopenharmony_ci pool_type, p_cur, p_max); 198762306a36Sopenharmony_ci} 198862306a36Sopenharmony_ci 198962306a36Sopenharmony_cistatic int felix_mrp_add(struct dsa_switch *ds, int port, 199062306a36Sopenharmony_ci const struct switchdev_obj_mrp *mrp) 199162306a36Sopenharmony_ci{ 199262306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 199362306a36Sopenharmony_ci 199462306a36Sopenharmony_ci return ocelot_mrp_add(ocelot, port, mrp); 199562306a36Sopenharmony_ci} 199662306a36Sopenharmony_ci 199762306a36Sopenharmony_cistatic int felix_mrp_del(struct dsa_switch *ds, int port, 199862306a36Sopenharmony_ci const struct switchdev_obj_mrp *mrp) 199962306a36Sopenharmony_ci{ 200062306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 200162306a36Sopenharmony_ci 200262306a36Sopenharmony_ci return ocelot_mrp_add(ocelot, port, mrp); 200362306a36Sopenharmony_ci} 200462306a36Sopenharmony_ci 200562306a36Sopenharmony_cistatic int 200662306a36Sopenharmony_cifelix_mrp_add_ring_role(struct dsa_switch *ds, int port, 200762306a36Sopenharmony_ci const struct switchdev_obj_ring_role_mrp *mrp) 200862306a36Sopenharmony_ci{ 200962306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 201062306a36Sopenharmony_ci 201162306a36Sopenharmony_ci return ocelot_mrp_add_ring_role(ocelot, port, mrp); 201262306a36Sopenharmony_ci} 201362306a36Sopenharmony_ci 201462306a36Sopenharmony_cistatic int 201562306a36Sopenharmony_cifelix_mrp_del_ring_role(struct dsa_switch *ds, int port, 201662306a36Sopenharmony_ci const struct switchdev_obj_ring_role_mrp *mrp) 201762306a36Sopenharmony_ci{ 201862306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 201962306a36Sopenharmony_ci 202062306a36Sopenharmony_ci return ocelot_mrp_del_ring_role(ocelot, port, mrp); 202162306a36Sopenharmony_ci} 202262306a36Sopenharmony_ci 202362306a36Sopenharmony_cistatic int felix_port_get_default_prio(struct dsa_switch *ds, int port) 202462306a36Sopenharmony_ci{ 202562306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 202662306a36Sopenharmony_ci 202762306a36Sopenharmony_ci return ocelot_port_get_default_prio(ocelot, port); 202862306a36Sopenharmony_ci} 202962306a36Sopenharmony_ci 203062306a36Sopenharmony_cistatic int felix_port_set_default_prio(struct dsa_switch *ds, int port, 203162306a36Sopenharmony_ci u8 prio) 203262306a36Sopenharmony_ci{ 203362306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 203462306a36Sopenharmony_ci 203562306a36Sopenharmony_ci return ocelot_port_set_default_prio(ocelot, port, prio); 203662306a36Sopenharmony_ci} 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_cistatic int felix_port_get_dscp_prio(struct dsa_switch *ds, int port, u8 dscp) 203962306a36Sopenharmony_ci{ 204062306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 204162306a36Sopenharmony_ci 204262306a36Sopenharmony_ci return ocelot_port_get_dscp_prio(ocelot, port, dscp); 204362306a36Sopenharmony_ci} 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_cistatic int felix_port_add_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, 204662306a36Sopenharmony_ci u8 prio) 204762306a36Sopenharmony_ci{ 204862306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 204962306a36Sopenharmony_ci 205062306a36Sopenharmony_ci return ocelot_port_add_dscp_prio(ocelot, port, dscp, prio); 205162306a36Sopenharmony_ci} 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_cistatic int felix_port_del_dscp_prio(struct dsa_switch *ds, int port, u8 dscp, 205462306a36Sopenharmony_ci u8 prio) 205562306a36Sopenharmony_ci{ 205662306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 205762306a36Sopenharmony_ci 205862306a36Sopenharmony_ci return ocelot_port_del_dscp_prio(ocelot, port, dscp, prio); 205962306a36Sopenharmony_ci} 206062306a36Sopenharmony_ci 206162306a36Sopenharmony_cistatic int felix_get_mm(struct dsa_switch *ds, int port, 206262306a36Sopenharmony_ci struct ethtool_mm_state *state) 206362306a36Sopenharmony_ci{ 206462306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 206562306a36Sopenharmony_ci 206662306a36Sopenharmony_ci return ocelot_port_get_mm(ocelot, port, state); 206762306a36Sopenharmony_ci} 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_cistatic int felix_set_mm(struct dsa_switch *ds, int port, 207062306a36Sopenharmony_ci struct ethtool_mm_cfg *cfg, 207162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 207262306a36Sopenharmony_ci{ 207362306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 207462306a36Sopenharmony_ci 207562306a36Sopenharmony_ci return ocelot_port_set_mm(ocelot, port, cfg, extack); 207662306a36Sopenharmony_ci} 207762306a36Sopenharmony_ci 207862306a36Sopenharmony_cistatic void felix_get_mm_stats(struct dsa_switch *ds, int port, 207962306a36Sopenharmony_ci struct ethtool_mm_stats *stats) 208062306a36Sopenharmony_ci{ 208162306a36Sopenharmony_ci struct ocelot *ocelot = ds->priv; 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_ci ocelot_port_get_mm_stats(ocelot, port, stats); 208462306a36Sopenharmony_ci} 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_ciconst struct dsa_switch_ops felix_switch_ops = { 208762306a36Sopenharmony_ci .get_tag_protocol = felix_get_tag_protocol, 208862306a36Sopenharmony_ci .change_tag_protocol = felix_change_tag_protocol, 208962306a36Sopenharmony_ci .connect_tag_protocol = felix_connect_tag_protocol, 209062306a36Sopenharmony_ci .setup = felix_setup, 209162306a36Sopenharmony_ci .teardown = felix_teardown, 209262306a36Sopenharmony_ci .set_ageing_time = felix_set_ageing_time, 209362306a36Sopenharmony_ci .get_mm = felix_get_mm, 209462306a36Sopenharmony_ci .set_mm = felix_set_mm, 209562306a36Sopenharmony_ci .get_mm_stats = felix_get_mm_stats, 209662306a36Sopenharmony_ci .get_stats64 = felix_get_stats64, 209762306a36Sopenharmony_ci .get_pause_stats = felix_get_pause_stats, 209862306a36Sopenharmony_ci .get_rmon_stats = felix_get_rmon_stats, 209962306a36Sopenharmony_ci .get_eth_ctrl_stats = felix_get_eth_ctrl_stats, 210062306a36Sopenharmony_ci .get_eth_mac_stats = felix_get_eth_mac_stats, 210162306a36Sopenharmony_ci .get_eth_phy_stats = felix_get_eth_phy_stats, 210262306a36Sopenharmony_ci .get_strings = felix_get_strings, 210362306a36Sopenharmony_ci .get_ethtool_stats = felix_get_ethtool_stats, 210462306a36Sopenharmony_ci .get_sset_count = felix_get_sset_count, 210562306a36Sopenharmony_ci .get_ts_info = felix_get_ts_info, 210662306a36Sopenharmony_ci .phylink_get_caps = felix_phylink_get_caps, 210762306a36Sopenharmony_ci .phylink_mac_config = felix_phylink_mac_config, 210862306a36Sopenharmony_ci .phylink_mac_select_pcs = felix_phylink_mac_select_pcs, 210962306a36Sopenharmony_ci .phylink_mac_link_down = felix_phylink_mac_link_down, 211062306a36Sopenharmony_ci .phylink_mac_link_up = felix_phylink_mac_link_up, 211162306a36Sopenharmony_ci .port_enable = felix_port_enable, 211262306a36Sopenharmony_ci .port_fast_age = felix_port_fast_age, 211362306a36Sopenharmony_ci .port_fdb_dump = felix_fdb_dump, 211462306a36Sopenharmony_ci .port_fdb_add = felix_fdb_add, 211562306a36Sopenharmony_ci .port_fdb_del = felix_fdb_del, 211662306a36Sopenharmony_ci .lag_fdb_add = felix_lag_fdb_add, 211762306a36Sopenharmony_ci .lag_fdb_del = felix_lag_fdb_del, 211862306a36Sopenharmony_ci .port_mdb_add = felix_mdb_add, 211962306a36Sopenharmony_ci .port_mdb_del = felix_mdb_del, 212062306a36Sopenharmony_ci .port_pre_bridge_flags = felix_pre_bridge_flags, 212162306a36Sopenharmony_ci .port_bridge_flags = felix_bridge_flags, 212262306a36Sopenharmony_ci .port_bridge_join = felix_bridge_join, 212362306a36Sopenharmony_ci .port_bridge_leave = felix_bridge_leave, 212462306a36Sopenharmony_ci .port_lag_join = felix_lag_join, 212562306a36Sopenharmony_ci .port_lag_leave = felix_lag_leave, 212662306a36Sopenharmony_ci .port_lag_change = felix_lag_change, 212762306a36Sopenharmony_ci .port_stp_state_set = felix_bridge_stp_state_set, 212862306a36Sopenharmony_ci .port_vlan_filtering = felix_vlan_filtering, 212962306a36Sopenharmony_ci .port_vlan_add = felix_vlan_add, 213062306a36Sopenharmony_ci .port_vlan_del = felix_vlan_del, 213162306a36Sopenharmony_ci .port_hwtstamp_get = felix_hwtstamp_get, 213262306a36Sopenharmony_ci .port_hwtstamp_set = felix_hwtstamp_set, 213362306a36Sopenharmony_ci .port_rxtstamp = felix_rxtstamp, 213462306a36Sopenharmony_ci .port_txtstamp = felix_txtstamp, 213562306a36Sopenharmony_ci .port_change_mtu = felix_change_mtu, 213662306a36Sopenharmony_ci .port_max_mtu = felix_get_max_mtu, 213762306a36Sopenharmony_ci .port_policer_add = felix_port_policer_add, 213862306a36Sopenharmony_ci .port_policer_del = felix_port_policer_del, 213962306a36Sopenharmony_ci .port_mirror_add = felix_port_mirror_add, 214062306a36Sopenharmony_ci .port_mirror_del = felix_port_mirror_del, 214162306a36Sopenharmony_ci .cls_flower_add = felix_cls_flower_add, 214262306a36Sopenharmony_ci .cls_flower_del = felix_cls_flower_del, 214362306a36Sopenharmony_ci .cls_flower_stats = felix_cls_flower_stats, 214462306a36Sopenharmony_ci .port_setup_tc = felix_port_setup_tc, 214562306a36Sopenharmony_ci .devlink_sb_pool_get = felix_sb_pool_get, 214662306a36Sopenharmony_ci .devlink_sb_pool_set = felix_sb_pool_set, 214762306a36Sopenharmony_ci .devlink_sb_port_pool_get = felix_sb_port_pool_get, 214862306a36Sopenharmony_ci .devlink_sb_port_pool_set = felix_sb_port_pool_set, 214962306a36Sopenharmony_ci .devlink_sb_tc_pool_bind_get = felix_sb_tc_pool_bind_get, 215062306a36Sopenharmony_ci .devlink_sb_tc_pool_bind_set = felix_sb_tc_pool_bind_set, 215162306a36Sopenharmony_ci .devlink_sb_occ_snapshot = felix_sb_occ_snapshot, 215262306a36Sopenharmony_ci .devlink_sb_occ_max_clear = felix_sb_occ_max_clear, 215362306a36Sopenharmony_ci .devlink_sb_occ_port_pool_get = felix_sb_occ_port_pool_get, 215462306a36Sopenharmony_ci .devlink_sb_occ_tc_port_bind_get= felix_sb_occ_tc_port_bind_get, 215562306a36Sopenharmony_ci .port_mrp_add = felix_mrp_add, 215662306a36Sopenharmony_ci .port_mrp_del = felix_mrp_del, 215762306a36Sopenharmony_ci .port_mrp_add_ring_role = felix_mrp_add_ring_role, 215862306a36Sopenharmony_ci .port_mrp_del_ring_role = felix_mrp_del_ring_role, 215962306a36Sopenharmony_ci .tag_8021q_vlan_add = felix_tag_8021q_vlan_add, 216062306a36Sopenharmony_ci .tag_8021q_vlan_del = felix_tag_8021q_vlan_del, 216162306a36Sopenharmony_ci .port_get_default_prio = felix_port_get_default_prio, 216262306a36Sopenharmony_ci .port_set_default_prio = felix_port_set_default_prio, 216362306a36Sopenharmony_ci .port_get_dscp_prio = felix_port_get_dscp_prio, 216462306a36Sopenharmony_ci .port_add_dscp_prio = felix_port_add_dscp_prio, 216562306a36Sopenharmony_ci .port_del_dscp_prio = felix_port_del_dscp_prio, 216662306a36Sopenharmony_ci .port_set_host_flood = felix_port_set_host_flood, 216762306a36Sopenharmony_ci .port_change_master = felix_port_change_master, 216862306a36Sopenharmony_ci}; 216962306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(felix_switch_ops); 217062306a36Sopenharmony_ci 217162306a36Sopenharmony_cistruct net_device *felix_port_to_netdev(struct ocelot *ocelot, int port) 217262306a36Sopenharmony_ci{ 217362306a36Sopenharmony_ci struct felix *felix = ocelot_to_felix(ocelot); 217462306a36Sopenharmony_ci struct dsa_switch *ds = felix->ds; 217562306a36Sopenharmony_ci 217662306a36Sopenharmony_ci if (!dsa_is_user_port(ds, port)) 217762306a36Sopenharmony_ci return NULL; 217862306a36Sopenharmony_ci 217962306a36Sopenharmony_ci return dsa_to_port(ds, port)->slave; 218062306a36Sopenharmony_ci} 218162306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(felix_port_to_netdev); 218262306a36Sopenharmony_ci 218362306a36Sopenharmony_ciint felix_netdev_to_port(struct net_device *dev) 218462306a36Sopenharmony_ci{ 218562306a36Sopenharmony_ci struct dsa_port *dp; 218662306a36Sopenharmony_ci 218762306a36Sopenharmony_ci dp = dsa_port_from_netdev(dev); 218862306a36Sopenharmony_ci if (IS_ERR(dp)) 218962306a36Sopenharmony_ci return -EINVAL; 219062306a36Sopenharmony_ci 219162306a36Sopenharmony_ci return dp->index; 219262306a36Sopenharmony_ci} 219362306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(felix_netdev_to_port); 219462306a36Sopenharmony_ci 219562306a36Sopenharmony_ciMODULE_DESCRIPTION("Felix DSA library"); 219662306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2197