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