Commit 56435d91 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'tag_8021q-for-ocelot-switches'

Vladimir Oltean says:

====================
tag_8021q for Ocelot switches

The Felix switch inside LS1028A has an issue. It has a 2.5G CPU port,
and the external ports, in the majority of use cases, run at 1G. This
means that, when the CPU injects traffic into the switch, it is very
easy to run into congestion. This is not to say that it is impossible to
enter congestion even with all ports running at the same speed, just
that the default configuration is already very prone to that by design.

Normally, the way to deal with that is using Ethernet flow control
(PAUSE frames).

However, this functionality is not working today with the ENETC - Felix
switch pair. The hardware issue is undergoing documentation right now as
an erratum within NXP, but several customers have been requesting a
reasonable workaround for it.

In truth, the LS1028A has 2 internal port pairs. The lack of flow control
is an issue only when NPI mode (Node Processor Interface, aka the mode
where the "CPU port module", which carries DSA-style tagged packets, is
connected to a regular Ethernet port) is used, and NPI mode is supported
by Felix on a single port.

In past BSPs, we have had setups where both internal port pairs were
enabled. We were advertising the following setup:

"data port"     "control port"
  (2.5G)            (1G)

   eno2             eno3
    ^                ^
    |                |
    | regular        | DSA-tagged
    | frames         | frames
    |                |
    v                v
   swp4             swp5

This works but is highly unpractical, due to NXP shifting the task of
designing a functional system (choosing which port to use, depending on
type of traffic required) up to the end user. The swpN interfaces would
have to be bridged with swp4, in order for the eno2 "data port" to have
access to the outside network. And the swpN interfaces would still be
capable of IP networking. So running a DHCP client would give us two IP
interfaces from the same subnet, one assigned to eno2, and the other to
swpN (0, 1, 2, 3).

Also, the dual port design doesn't scale. When attaching another DSA
switch to a Felix port, the end result is that the "data port" cannot
carry any meaningful data to the external world, since it lacks the DSA
tags required to traverse the sja1105 switches below. All that traffic
needs to go through the "control port".

So in newer BSPs there was a desire to simplify that setup, and only
have one internal port pair:

   eno2            eno3
    ^
    |
    | DSA-tagged    x disabled
    | frames
    |
    v
   swp4            swp5

However, this setup only exacerbates the issue of not having flow
control on the NPI port, since that is the only port now. Also, there
are use cases that still require the "data port", such as IEEE 802.1CB
(TSN stream identification doesn't work over an NPI port), source
MAC address learning over NPI, etc.

Again, there is a desire to keep the simplicity of the single internal
port setup, while regaining the benefits of having a dedicated data port
as well. And this series attempts to deliver just that.

So the NPI functionality is disabled conditionally. Its purpose was:
- To ensure individually addressable ports on TX. This can be replaced
  by using some designated VLAN tags which are pushed by the DSA tagger
  code, then removed by the switch (so they are invisible to the outside
  world and to the user).
- To ensure source port identification on RX. Again, this can be
  replaced by using some designated VLAN tags to encapsulate all RX
  traffic (each VLAN uniquely identifies a source port). The DSA tagger
  determines which port it was based on the VLAN number, then removes
  that header.
- To deliver PTP timestamps. This cannot be obtained through VLAN
  headers, so we need to take a step back and see how else we can do
  that. The Microchip Ocelot-1 (VSC7514 MIPS) driver performs manual
  injection/extraction from the CPU port module using register-based
  MMIO, and not over Ethernet. We will need to do the same from DSA,
  which makes this tagger a sort of hybrid between DSA and pure
  switchdev.
====================

Link: https://lore.kernel.org/r/20210129010009.3959398-1-olteanv@gmail.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents de1da8bc e21268ef
Loading
Loading
Loading
Loading
+9 −2
Original line number Diff line number Diff line
@@ -3,5 +3,12 @@ Date: August 2018
KernelVersion:	4.20
Contact:	netdev@vger.kernel.org
Description:
		String indicating the type of tagging protocol used by the
		DSA slave network device.
		On read, this file returns a string indicating the type of
		tagging protocol used by the DSA network devices that are
		attached to this master interface.
		On write, this file changes the tagging protocol of the
		attached DSA switches, if this operation is supported by the
		driver. Changing the tagging protocol must be done with the DSA
		interfaces and the master interface all administratively down.
		See the "name" field of each registered struct dsa_device_ops
		for a list of valid values.
+1 −0
Original line number Diff line number Diff line
@@ -12859,6 +12859,7 @@ F: drivers/net/dsa/ocelot/*
F:	drivers/net/ethernet/mscc/
F:	include/soc/mscc/ocelot*
F:	net/dsa/tag_ocelot.c
F:	net/dsa/tag_ocelot_8021q.c
F:	tools/testing/selftests/drivers/net/ocelot/*
OCXL (Open Coherent Accelerator Processor Interface OpenCAPI) DRIVER
+2 −0
Original line number Diff line number Diff line
@@ -6,6 +6,7 @@ config NET_DSA_MSCC_FELIX
	depends on NET_VENDOR_FREESCALE
	depends on HAS_IOMEM
	select MSCC_OCELOT_SWITCH_LIB
	select NET_DSA_TAG_OCELOT_8021Q
	select NET_DSA_TAG_OCELOT
	select FSL_ENETC_MDIO
	select PCS_LYNX
@@ -19,6 +20,7 @@ config NET_DSA_MSCC_SEVILLE
	depends on NET_VENDOR_MICROSEMI
	depends on HAS_IOMEM
	select MSCC_OCELOT_SWITCH_LIB
	select NET_DSA_TAG_OCELOT_8021Q
	select NET_DSA_TAG_OCELOT
	select PCS_LYNX
	help
+488 −37
Original line number Diff line number Diff line
// SPDX-License-Identifier: GPL-2.0
/* Copyright 2019 NXP Semiconductors
/* Copyright 2019-2021 NXP Semiconductors
 *
 * This is an umbrella module for all network switches that are
 * register-compatible with Ocelot and that perform I/O to their host CPU
@@ -13,6 +13,7 @@
#include <soc/mscc/ocelot_ana.h>
#include <soc/mscc/ocelot_ptp.h>
#include <soc/mscc/ocelot.h>
#include <linux/dsa/8021q.h>
#include <linux/platform_device.h>
#include <linux/packing.h>
#include <linux/module.h>
@@ -24,11 +25,474 @@
#include <net/dsa.h>
#include "felix.h"

static int felix_tag_8021q_rxvlan_add(struct felix *felix, int port, u16 vid,
				      bool pvid, bool untagged)
{
	struct ocelot_vcap_filter *outer_tagging_rule;
	struct ocelot *ocelot = &felix->ocelot;
	struct dsa_switch *ds = felix->ds;
	int key_length, upstream, err;

	/* We don't need to install the rxvlan into the other ports' filtering
	 * tables, because we're just pushing the rxvlan when sending towards
	 * the CPU
	 */
	if (!pvid)
		return 0;

	key_length = ocelot->vcap[VCAP_ES0].keys[VCAP_ES0_IGR_PORT].length;
	upstream = dsa_upstream_port(ds, port);

	outer_tagging_rule = kzalloc(sizeof(struct ocelot_vcap_filter),
				     GFP_KERNEL);
	if (!outer_tagging_rule)
		return -ENOMEM;

	outer_tagging_rule->key_type = OCELOT_VCAP_KEY_ANY;
	outer_tagging_rule->prio = 1;
	outer_tagging_rule->id.cookie = port;
	outer_tagging_rule->id.tc_offload = false;
	outer_tagging_rule->block_id = VCAP_ES0;
	outer_tagging_rule->type = OCELOT_VCAP_FILTER_OFFLOAD;
	outer_tagging_rule->lookup = 0;
	outer_tagging_rule->ingress_port.value = port;
	outer_tagging_rule->ingress_port.mask = GENMASK(key_length - 1, 0);
	outer_tagging_rule->egress_port.value = upstream;
	outer_tagging_rule->egress_port.mask = GENMASK(key_length - 1, 0);
	outer_tagging_rule->action.push_outer_tag = OCELOT_ES0_TAG;
	outer_tagging_rule->action.tag_a_tpid_sel = OCELOT_TAG_TPID_SEL_8021AD;
	outer_tagging_rule->action.tag_a_vid_sel = 1;
	outer_tagging_rule->action.vid_a_val = vid;

	err = ocelot_vcap_filter_add(ocelot, outer_tagging_rule, NULL);
	if (err)
		kfree(outer_tagging_rule);

	return err;
}

static int felix_tag_8021q_txvlan_add(struct felix *felix, int port, u16 vid,
				      bool pvid, bool untagged)
{
	struct ocelot_vcap_filter *untagging_rule, *redirect_rule;
	struct ocelot *ocelot = &felix->ocelot;
	struct dsa_switch *ds = felix->ds;
	int upstream, err;

	/* tag_8021q.c assumes we are implementing this via port VLAN
	 * membership, which we aren't. So we don't need to add any VCAP filter
	 * for the CPU port.
	 */
	if (ocelot->ports[port]->is_dsa_8021q_cpu)
		return 0;

	untagging_rule = kzalloc(sizeof(struct ocelot_vcap_filter), GFP_KERNEL);
	if (!untagging_rule)
		return -ENOMEM;

	redirect_rule = kzalloc(sizeof(struct ocelot_vcap_filter), GFP_KERNEL);
	if (!redirect_rule) {
		kfree(untagging_rule);
		return -ENOMEM;
	}

	upstream = dsa_upstream_port(ds, port);

	untagging_rule->key_type = OCELOT_VCAP_KEY_ANY;
	untagging_rule->ingress_port_mask = BIT(upstream);
	untagging_rule->vlan.vid.value = vid;
	untagging_rule->vlan.vid.mask = VLAN_VID_MASK;
	untagging_rule->prio = 1;
	untagging_rule->id.cookie = port;
	untagging_rule->id.tc_offload = false;
	untagging_rule->block_id = VCAP_IS1;
	untagging_rule->type = OCELOT_VCAP_FILTER_OFFLOAD;
	untagging_rule->lookup = 0;
	untagging_rule->action.vlan_pop_cnt_ena = true;
	untagging_rule->action.vlan_pop_cnt = 1;
	untagging_rule->action.pag_override_mask = 0xff;
	untagging_rule->action.pag_val = port;

	err = ocelot_vcap_filter_add(ocelot, untagging_rule, NULL);
	if (err) {
		kfree(untagging_rule);
		kfree(redirect_rule);
		return err;
	}

	redirect_rule->key_type = OCELOT_VCAP_KEY_ANY;
	redirect_rule->ingress_port_mask = BIT(upstream);
	redirect_rule->pag = port;
	redirect_rule->prio = 1;
	redirect_rule->id.cookie = port;
	redirect_rule->id.tc_offload = false;
	redirect_rule->block_id = VCAP_IS2;
	redirect_rule->type = OCELOT_VCAP_FILTER_OFFLOAD;
	redirect_rule->lookup = 0;
	redirect_rule->action.mask_mode = OCELOT_MASK_MODE_REDIRECT;
	redirect_rule->action.port_mask = BIT(port);

	err = ocelot_vcap_filter_add(ocelot, redirect_rule, NULL);
	if (err) {
		ocelot_vcap_filter_del(ocelot, untagging_rule);
		kfree(redirect_rule);
		return err;
	}

	return 0;
}

static int felix_tag_8021q_vlan_add(struct dsa_switch *ds, int port, u16 vid,
				    u16 flags)
{
	bool untagged = flags & BRIDGE_VLAN_INFO_UNTAGGED;
	bool pvid = flags & BRIDGE_VLAN_INFO_PVID;
	struct ocelot *ocelot = ds->priv;

	if (vid_is_dsa_8021q_rxvlan(vid))
		return felix_tag_8021q_rxvlan_add(ocelot_to_felix(ocelot),
						  port, vid, pvid, untagged);

	if (vid_is_dsa_8021q_txvlan(vid))
		return felix_tag_8021q_txvlan_add(ocelot_to_felix(ocelot),
						  port, vid, pvid, untagged);

	return 0;
}

static int felix_tag_8021q_rxvlan_del(struct felix *felix, int port, u16 vid)
{
	struct ocelot_vcap_filter *outer_tagging_rule;
	struct ocelot_vcap_block *block_vcap_es0;
	struct ocelot *ocelot = &felix->ocelot;

	block_vcap_es0 = &ocelot->block[VCAP_ES0];

	outer_tagging_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_es0,
								 port, false);
	/* In rxvlan_add, we had the "if (!pvid) return 0" logic to avoid
	 * installing outer tagging ES0 rules where they weren't needed.
	 * But in rxvlan_del, the API doesn't give us the "flags" anymore,
	 * so that forces us to be slightly sloppy here, and just assume that
	 * if we didn't find an outer_tagging_rule it means that there was
	 * none in the first place, i.e. rxvlan_del is called on a non-pvid
	 * port. This is most probably true though.
	 */
	if (!outer_tagging_rule)
		return 0;

	return ocelot_vcap_filter_del(ocelot, outer_tagging_rule);
}

static int felix_tag_8021q_txvlan_del(struct felix *felix, int port, u16 vid)
{
	struct ocelot_vcap_filter *untagging_rule, *redirect_rule;
	struct ocelot_vcap_block *block_vcap_is1;
	struct ocelot_vcap_block *block_vcap_is2;
	struct ocelot *ocelot = &felix->ocelot;
	int err;

	if (ocelot->ports[port]->is_dsa_8021q_cpu)
		return 0;

	block_vcap_is1 = &ocelot->block[VCAP_IS1];
	block_vcap_is2 = &ocelot->block[VCAP_IS2];

	untagging_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_is1,
							     port, false);
	if (!untagging_rule)
		return 0;

	err = ocelot_vcap_filter_del(ocelot, untagging_rule);
	if (err)
		return err;

	redirect_rule = ocelot_vcap_block_find_filter_by_id(block_vcap_is2,
							    port, false);
	if (!redirect_rule)
		return 0;

	return ocelot_vcap_filter_del(ocelot, redirect_rule);
}

static int felix_tag_8021q_vlan_del(struct dsa_switch *ds, int port, u16 vid)
{
	struct ocelot *ocelot = ds->priv;

	if (vid_is_dsa_8021q_rxvlan(vid))
		return felix_tag_8021q_rxvlan_del(ocelot_to_felix(ocelot),
						  port, vid);

	if (vid_is_dsa_8021q_txvlan(vid))
		return felix_tag_8021q_txvlan_del(ocelot_to_felix(ocelot),
						  port, vid);

	return 0;
}

static const struct dsa_8021q_ops felix_tag_8021q_ops = {
	.vlan_add	= felix_tag_8021q_vlan_add,
	.vlan_del	= felix_tag_8021q_vlan_del,
};

/* Alternatively to using the NPI functionality, that same hardware MAC
 * connected internally to the enetc or fman DSA master can be configured to
 * use the software-defined tag_8021q frame format. As far as the hardware is
 * concerned, it thinks it is a "dumb switch" - the queues of the CPU port
 * module are now disconnected from it, but can still be accessed through
 * register-based MMIO.
 */
static void felix_8021q_cpu_port_init(struct ocelot *ocelot, int port)
{
	ocelot->ports[port]->is_dsa_8021q_cpu = true;
	ocelot->npi = -1;

	/* Overwrite PGID_CPU with the non-tagging port */
	ocelot_write_rix(ocelot, BIT(port), ANA_PGID_PGID, PGID_CPU);

	ocelot_apply_bridge_fwd_mask(ocelot);
}

static void felix_8021q_cpu_port_deinit(struct ocelot *ocelot, int port)
{
	ocelot->ports[port]->is_dsa_8021q_cpu = false;

	/* Restore PGID_CPU */
	ocelot_write_rix(ocelot, BIT(ocelot->num_phys_ports), ANA_PGID_PGID,
			 PGID_CPU);

	ocelot_apply_bridge_fwd_mask(ocelot);
}

static int felix_setup_tag_8021q(struct dsa_switch *ds, int cpu)
{
	struct ocelot *ocelot = ds->priv;
	struct felix *felix = ocelot_to_felix(ocelot);
	unsigned long cpu_flood;
	int port, err;

	felix_8021q_cpu_port_init(ocelot, cpu);

	for (port = 0; port < ds->num_ports; port++) {
		if (dsa_is_unused_port(ds, port))
			continue;

		/* This overwrites ocelot_init():
		 * Do not forward BPDU frames to the CPU port module,
		 * for 2 reasons:
		 * - When these packets are injected from the tag_8021q
		 *   CPU port, we want them to go out, not loop back
		 *   into the system.
		 * - STP traffic ingressing on a user port should go to
		 *   the tag_8021q CPU port, not to the hardware CPU
		 *   port module.
		 */
		ocelot_write_gix(ocelot,
				 ANA_PORT_CPU_FWD_BPDU_CFG_BPDU_REDIR_ENA(0),
				 ANA_PORT_CPU_FWD_BPDU_CFG, port);
	}

	/* In tag_8021q mode, the CPU port module is unused. So we
	 * want to disable flooding of any kind to the CPU port module,
	 * since packets going there will end in a black hole.
	 */
	cpu_flood = ANA_PGID_PGID_PGID(BIT(ocelot->num_phys_ports));
	ocelot_rmw_rix(ocelot, 0, cpu_flood, ANA_PGID_PGID, PGID_UC);
	ocelot_rmw_rix(ocelot, 0, cpu_flood, ANA_PGID_PGID, PGID_MC);

	felix->dsa_8021q_ctx = kzalloc(sizeof(*felix->dsa_8021q_ctx),
				       GFP_KERNEL);
	if (!felix->dsa_8021q_ctx)
		return -ENOMEM;

	felix->dsa_8021q_ctx->ops = &felix_tag_8021q_ops;
	felix->dsa_8021q_ctx->proto = htons(ETH_P_8021AD);
	felix->dsa_8021q_ctx->ds = ds;

	err = dsa_8021q_setup(felix->dsa_8021q_ctx, true);
	if (err)
		goto out_free_dsa_8021_ctx;

	return 0;

out_free_dsa_8021_ctx:
	kfree(felix->dsa_8021q_ctx);
	return err;
}

static void felix_teardown_tag_8021q(struct dsa_switch *ds, int cpu)
{
	struct ocelot *ocelot = ds->priv;
	struct felix *felix = ocelot_to_felix(ocelot);
	int err, port;

	err = dsa_8021q_setup(felix->dsa_8021q_ctx, false);
	if (err)
		dev_err(ds->dev, "dsa_8021q_setup returned %d", err);

	kfree(felix->dsa_8021q_ctx);

	for (port = 0; port < ds->num_ports; port++) {
		if (dsa_is_unused_port(ds, port))
			continue;

		/* Restore the logic from ocelot_init:
		 * do not forward BPDU frames to the front ports.
		 */
		ocelot_write_gix(ocelot,
				 ANA_PORT_CPU_FWD_BPDU_CFG_BPDU_REDIR_ENA(0xffff),
				 ANA_PORT_CPU_FWD_BPDU_CFG,
				 port);
	}

	felix_8021q_cpu_port_deinit(ocelot, cpu);
}

/* The CPU port module is connected to the Node Processor Interface (NPI). This
 * is the mode through which frames can be injected from and extracted to an
 * external CPU, over Ethernet. In NXP SoCs, the "external CPU" is the ARM CPU
 * running Linux, and this forms a DSA setup together with the enetc or fman
 * DSA master.
 */
static void felix_npi_port_init(struct ocelot *ocelot, int port)
{
	ocelot->npi = port;

	ocelot_write(ocelot, QSYS_EXT_CPU_CFG_EXT_CPUQ_MSK_M |
		     QSYS_EXT_CPU_CFG_EXT_CPU_PORT(port),
		     QSYS_EXT_CPU_CFG);

	/* NPI port Injection/Extraction configuration */
	ocelot_fields_write(ocelot, port, SYS_PORT_MODE_INCL_XTR_HDR,
			    ocelot->npi_xtr_prefix);
	ocelot_fields_write(ocelot, port, SYS_PORT_MODE_INCL_INJ_HDR,
			    ocelot->npi_inj_prefix);

	/* Disable transmission of pause frames */
	ocelot_fields_write(ocelot, port, SYS_PAUSE_CFG_PAUSE_ENA, 0);
}

static void felix_npi_port_deinit(struct ocelot *ocelot, int port)
{
	/* Restore hardware defaults */
	int unused_port = ocelot->num_phys_ports + 2;

	ocelot->npi = -1;

	ocelot_write(ocelot, QSYS_EXT_CPU_CFG_EXT_CPU_PORT(unused_port),
		     QSYS_EXT_CPU_CFG);

	ocelot_fields_write(ocelot, port, SYS_PORT_MODE_INCL_XTR_HDR,
			    OCELOT_TAG_PREFIX_DISABLED);
	ocelot_fields_write(ocelot, port, SYS_PORT_MODE_INCL_INJ_HDR,
			    OCELOT_TAG_PREFIX_DISABLED);

	/* Enable transmission of pause frames */
	ocelot_fields_write(ocelot, port, SYS_PAUSE_CFG_PAUSE_ENA, 1);
}

static int felix_setup_tag_npi(struct dsa_switch *ds, int cpu)
{
	struct ocelot *ocelot = ds->priv;
	unsigned long cpu_flood;

	felix_npi_port_init(ocelot, cpu);

	/* Include the CPU port module (and indirectly, the NPI port)
	 * in the forwarding mask for unknown unicast - the hardware
	 * default value for ANA_FLOODING_FLD_UNICAST excludes
	 * BIT(ocelot->num_phys_ports), and so does ocelot_init,
	 * since Ocelot relies on whitelisting MAC addresses towards
	 * PGID_CPU.
	 * We do this because DSA does not yet perform RX filtering,
	 * and the NPI port does not perform source address learning,
	 * so traffic sent to Linux is effectively unknown from the
	 * switch's perspective.
	 */
	cpu_flood = ANA_PGID_PGID_PGID(BIT(ocelot->num_phys_ports));
	ocelot_rmw_rix(ocelot, cpu_flood, cpu_flood, ANA_PGID_PGID, PGID_UC);

	return 0;
}

static void felix_teardown_tag_npi(struct dsa_switch *ds, int cpu)
{
	struct ocelot *ocelot = ds->priv;

	felix_npi_port_deinit(ocelot, cpu);
}

static int felix_set_tag_protocol(struct dsa_switch *ds, int cpu,
				  enum dsa_tag_protocol proto)
{
	int err;

	switch (proto) {
	case DSA_TAG_PROTO_OCELOT:
		err = felix_setup_tag_npi(ds, cpu);
		break;
	case DSA_TAG_PROTO_OCELOT_8021Q:
		err = felix_setup_tag_8021q(ds, cpu);
		break;
	default:
		err = -EPROTONOSUPPORT;
	}

	return err;
}

static void felix_del_tag_protocol(struct dsa_switch *ds, int cpu,
				   enum dsa_tag_protocol proto)
{
	switch (proto) {
	case DSA_TAG_PROTO_OCELOT:
		felix_teardown_tag_npi(ds, cpu);
		break;
	case DSA_TAG_PROTO_OCELOT_8021Q:
		felix_teardown_tag_8021q(ds, cpu);
		break;
	default:
		break;
	}
}

/* This always leaves the switch in a consistent state, because although the
 * tag_8021q setup can fail, the NPI setup can't. So either the change is made,
 * or the restoration is guaranteed to work.
 */
static int felix_change_tag_protocol(struct dsa_switch *ds, int cpu,
				     enum dsa_tag_protocol proto)
{
	struct ocelot *ocelot = ds->priv;
	struct felix *felix = ocelot_to_felix(ocelot);
	enum dsa_tag_protocol old_proto = felix->tag_proto;
	int err;

	if (proto != DSA_TAG_PROTO_OCELOT &&
	    proto != DSA_TAG_PROTO_OCELOT_8021Q)
		return -EPROTONOSUPPORT;

	felix_del_tag_protocol(ds, cpu, old_proto);

	err = felix_set_tag_protocol(ds, cpu, proto);
	if (err) {
		felix_set_tag_protocol(ds, cpu, old_proto);
		return err;
	}

	felix->tag_proto = proto;

	return 0;
}

static enum dsa_tag_protocol felix_get_tag_protocol(struct dsa_switch *ds,
						    int port,
						    enum dsa_tag_protocol mp)
{
	return DSA_TAG_PROTO_OCELOT;
	struct ocelot *ocelot = ds->priv;
	struct felix *felix = ocelot_to_felix(ocelot);

	return felix->tag_proto;
}

static int felix_set_ageing_time(struct dsa_switch *ds,
@@ -425,8 +889,8 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)
	ocelot->num_mact_rows	= felix->info->num_mact_rows;
	ocelot->vcap		= felix->info->vcap;
	ocelot->ops		= felix->info->ops;
	ocelot->inj_prefix	= OCELOT_TAG_PREFIX_SHORT;
	ocelot->xtr_prefix	= OCELOT_TAG_PREFIX_SHORT;
	ocelot->npi_inj_prefix	= OCELOT_TAG_PREFIX_SHORT;
	ocelot->npi_xtr_prefix	= OCELOT_TAG_PREFIX_SHORT;
	ocelot->devlink		= felix->ds->devlink;

	port_phy_modes = kcalloc(num_phys_ports, sizeof(phy_interface_t),
@@ -527,28 +991,6 @@ static int felix_init_structs(struct felix *felix, int num_phys_ports)
	return 0;
}

/* The CPU port module is connected to the Node Processor Interface (NPI). This
 * is the mode through which frames can be injected from and extracted to an
 * external CPU, over Ethernet.
 */
static void felix_npi_port_init(struct ocelot *ocelot, int port)
{
	ocelot->npi = port;

	ocelot_write(ocelot, QSYS_EXT_CPU_CFG_EXT_CPUQ_MSK_M |
		     QSYS_EXT_CPU_CFG_EXT_CPU_PORT(port),
		     QSYS_EXT_CPU_CFG);

	/* NPI port Injection/Extraction configuration */
	ocelot_fields_write(ocelot, port, SYS_PORT_MODE_INCL_XTR_HDR,
			    ocelot->xtr_prefix);
	ocelot_fields_write(ocelot, port, SYS_PORT_MODE_INCL_INJ_HDR,
			    ocelot->inj_prefix);

	/* Disable transmission of pause frames */
	ocelot_fields_write(ocelot, port, SYS_PAUSE_CFG_PAUSE_ENA, 0);
}

/* Hardware initialization done here so that we can allocate structures with
 * devm without fear of dsa_register_switch returning -EPROBE_DEFER and causing
 * us to allocate structures twice (leak memory) and map PCI memory twice
@@ -578,10 +1020,10 @@ static int felix_setup(struct dsa_switch *ds)
	}

	for (port = 0; port < ds->num_ports; port++) {
		ocelot_init_port(ocelot, port);
		if (dsa_is_unused_port(ds, port))
			continue;

		if (dsa_is_cpu_port(ds, port))
			felix_npi_port_init(ocelot, port);
		ocelot_init_port(ocelot, port);

		/* Set the default QoS Classification based on PCP and DEI
		 * bits of vlan tag.
@@ -593,14 +1035,15 @@ static int felix_setup(struct dsa_switch *ds)
	if (err)
		return err;

	/* Include the CPU port module in the forwarding mask for unknown
	 * unicast - the hardware default value for ANA_FLOODING_FLD_UNICAST
	 * excludes BIT(ocelot->num_phys_ports), and so does ocelot_init, since
	 * Ocelot relies on whitelisting MAC addresses towards PGID_CPU.
	for (port = 0; port < ds->num_ports; port++) {
		if (!dsa_is_cpu_port(ds, port))
			continue;

		/* The initial tag protocol is NPI which always returns 0, so
		 * there's no real point in checking for errors.
		 */
	ocelot_write_rix(ocelot,
			 ANA_PGID_PGID_PGID(GENMASK(ocelot->num_phys_ports, 0)),
			 ANA_PGID_PGID, PGID_UC);
		felix_set_tag_protocol(ds, port, felix->tag_proto);
	}

	ds->mtu_enforcement_ingress = true;
	ds->assisted_learning_on_cpu_port = true;
@@ -614,6 +1057,13 @@ static void felix_teardown(struct dsa_switch *ds)
	struct felix *felix = ocelot_to_felix(ocelot);
	int port;

	for (port = 0; port < ds->num_ports; port++) {
		if (!dsa_is_cpu_port(ds, port))
			continue;

		felix_del_tag_protocol(ds, port, felix->tag_proto);
	}

	ocelot_devlink_sb_unregister(ocelot);
	ocelot_deinit_timestamp(ocelot);
	ocelot_deinit(ocelot);
@@ -860,6 +1310,7 @@ static int felix_sb_occ_tc_port_bind_get(struct dsa_switch *ds, int port,

const struct dsa_switch_ops felix_switch_ops = {
	.get_tag_protocol		= felix_get_tag_protocol,
	.change_tag_protocol		= felix_change_tag_protocol,
	.setup				= felix_setup,
	.teardown			= felix_teardown,
	.set_ageing_time		= felix_set_ageing_time,
+2 −0
Original line number Diff line number Diff line
@@ -48,6 +48,8 @@ struct felix {
	struct lynx_pcs			**pcs;
	resource_size_t			switch_base;
	resource_size_t			imdio_base;
	struct dsa_8021q_context	*dsa_8021q_ctx;
	enum dsa_tag_protocol		tag_proto;
};

struct net_device *felix_port_to_netdev(struct ocelot *ocelot, int port);
Loading